一个转账的问题
创建表
添加数据
实体类
逆向生成实体类,并添加无参构造、带参构造、toString
package com.qzcsbj.bean; public class Account { private long id; private String username; private double money; public Account() { } public Account(String username, double money) { this.username = username; this.money = money; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public double getMoney() { return money; } public void setMoney(double money) { this.money = money; } @Override public String toString() { return "Account{" + "id=" + id + ", username='" + username + '\'' + ", money=" + money + '}'; } }
dao层(mapper层)
mapper接口
package com.qzcsbj.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.qzcsbj.bean.Account; /** * @公众号 : 全栈测试笔记 * @博客 : www.cnblogs.com/uncleyong * @微信 : ren168632201 * @描述 : <> */ public interface AccountMapper extends BaseMapper<Account> { }
service层
service接口
package com.qzcsbj.service; import com.baomidou.mybatisplus.extension.service.IService; import com.qzcsbj.bean.Account; /** * @公众号 : 全栈测试笔记 * @博客 : www.cnblogs.com/uncleyong * @微信 : ren168632201 * @描述 : <> */ public interface AccountService extends IService<Account> { // 这里只是演示,假设username是唯一的 public int transferMoney(String fromName,String toName,double money); }
service实现类
package com.qzcsbj.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.qzcsbj.bean.Account; import com.qzcsbj.mapper.AccountMapper; import com.qzcsbj.service.AccountService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements AccountService { @Autowired AccountMapper accountMapper; public int transferMoney(String fromName, String toName, double money) { // 账户A给账户B转钱,这里只是演示,假设账户A的钱≥要转出的钱 // 账户A Account accountA = accountMapper.selectOne(new QueryWrapper<Account>().eq("username", fromName)); accountA.setMoney(accountA.getMoney()-money); int countA = accountMapper.updateById(accountA); // int countA = accountMapper.update(accountA,new QueryWrapper<Account>().eq("username",fromName)); // 账户B Account accountB = accountMapper.selectOne(new QueryWrapper<Account>().eq("username", toName)); accountB.setMoney(accountB.getMoney()+money); int countB = accountMapper.updateById(accountB); // int countB = accountMapper.update(accountB,new QueryWrapper<Account>().eq("username",toName)); return countA+countB; } }
测试类
package com.qzcsbj.test; import com.qzcsbj.service.AccountService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * @公众号 : 全栈测试笔记 * @博客 : www.cnblogs.com/uncleyong * @微信 : ren168632201 * @描述 : <> */ @RunWith(SpringJUnit4ClassRunner.class) // 表示Spring和JUnit整合测试 @ContextConfiguration("classpath:applicationContext.xml") public class TestTransaction { @Autowired AccountService accountService; @Test public void testTransferMoney(){ String fromName = "jack"; String toName = "tom"; double money = 10.0; int i = accountService.transferMoney(fromName, toName, money); if (i==2){ System.out.println(fromName + "给"+ toName + "转账" + money + "元成功"); } else { System.out.println(fromName + "给"+ toName + "转账" + money + "元成功"); } } }
结果
表中数据正确
模拟异常
修改service实现类,模拟异常
package com.qzcsbj.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.qzcsbj.bean.Account; import com.qzcsbj.mapper.AccountMapper; import com.qzcsbj.service.AccountService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements AccountService { @Autowired AccountMapper accountMapper; public int transferMioney(String fromName, String toName, double money) { // 账户A给账户B转钱,这里只是演示,假设账户A的钱≥要转出的钱 // 账户A Account accountA = accountMapper.selectOne(new QueryWrapper<Account>().eq("username", fromName)); accountA.setMoney(accountA.getMoney()-money); int countA = accountMapper.updateById(accountA); // int countA = accountMapper.update(accountA,new QueryWrapper<Account>().eq("username",fromName)); // 模拟异常 int i=1/0; // 账户B Account accountB = accountMapper.selectOne(new QueryWrapper<Account>().eq("username", toName)); accountB.setMoney(accountB.getMoney()+money); int countB = accountMapper.updateById(accountB); // int countB = accountMapper.update(accountB,new QueryWrapper<Account>().eq("username",toName)); return countA+countB; } }
测试结果
运行出异常了,jack的账户扣了钱(mybatis底层是jdbc,默认自动提交事务,所以转出这个操作会成功入库),tom的账户没变
所以,transferMoney这个业务方法需要在事务中运行才可以。
事务(Transaction)介绍
事务是一系列的动作,是一个完整的工作单元,这些动作必须全部完成,如果有一个失败的话,那么事务就会回滚到最开始的状态。
在企业级应用程序开发中,事务管理是必不可少的技术,用来确保数据的完整性和一致性。
Spring事务管理的核心接口
org.springframework.transaction下三个接口是Spring中事务的顶级接口:平台事务管理器、事务定义接口、事务状态接口
mybatis底层是jdbc,所以需要配置DataSourceTransactionManager这个类
配置声明式事务:基于aop的xml配置
dao层,修改UserMapper,添加两个方法
package com.qzcsbj.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.qzcsbj.bean.User; import java.util.List; /** * @公众号 : 全栈测试笔记 * @博客 : www.cnblogs.com/uncleyong * @微信 : ren168632201 * @描述 : <> */ public interface UserMapper extends BaseMapper<User> { public List<User> getUsers(); public int addUser(User user); }
添加映射文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.qzcsbj.mapper.UserMapper"> <select id="getUsers" resultType="com.qzcsbj.bean.User"> select * from user </select> <insert id="addUser"> insert into user values(null,#{username},#{password},#{realName},#{sex},#{birthday},#{phone},#{utype},#{addtime},#{adduser}) </insert> </mapper>
service接口添加两个方法
package com.qzcsbj.service; import com.baomidou.mybatisplus.extension.service.IService; import com.qzcsbj.bean.User; import java.util.List; /** * @公众号 : 全栈测试笔记 * @博客 : www.cnblogs.com/uncleyong * @微信 : ren168632201 * @描述 : <> */ public interface UserService extends IService<User> { public List<User> getUsers(); public int addUser(User user); }
修改service实现类
package com.qzcsbj.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.qzcsbj.bean.User; import com.qzcsbj.mapper.UserMapper; import com.qzcsbj.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; /** * @公众号 : 全栈测试笔记 * @博客 : www.cnblogs.com/uncleyong * @微信 : ren168632201 * @描述 : <> */ @Service public class UserServiceImlp extends ServiceImpl<UserMapper,User> implements UserService { @Autowired UserMapper userMapper; public List<User> getUsers() { return userMapper.getUsers(); } public int addUser(User user) { return userMapper.addUser(user); } }
配置applicationContext.xml(基于这里<https://www.cnblogs.com/uncleyong/p/17026215.html>的xml添加如下内容)
下面只配置了一个方法需要织入事务
<tx:advice id="adviser" transaction-manager="transactionManager"> <!--配置哪些方法需要配代理织入事务--> <tx:attributes> <tx:method name="transferMoney"/> </tx:attributes> </tx:advice>
配置AOP切入点
<aop:config> <aop:pointcut id="pointcut" expression="execution(* com.qzcsbj.service.impl.*.*(..))"/> <aop:advisor advice-ref="adviser" pointcut-ref="pointcut"/> </aop:config>
测试报错
数据库回滚了,说明事务起作用了
其它配置
propagation:事务的传播性,非必须,默认值REQUIRED
isolation:隔离级别,非必须,mysql填可重复读REPEATABLE_READ;如果不配置就是默认isolation="DEFAULT"
timeout="-1",事务超时时间,单位是秒,非必须;默认值是-1,使用数据库底层的超时时间,如果底层数据库事务系统没有设置超时值,那么就是none,表示没有超时限制,也就是不超时;
read-only:只读,非必须,默认值是false,查询设置true,非查询设置false
no-rollback-for:非必须,示不被触发进行回滚的 Exception(s)
rollback-for:非必须,表示将被触发进行回滚的 Exception(s)
配置声明式事务:基于aop的注解配置
applicationContext.xml配置(基于这里<https://www.cnblogs.com/uncleyong/p/17026215.html>的xml添加如下内容)
<!--配置MyBatis的事务平台管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="ds"/> </bean> <!--开启注解管理事务--> <tx:annotation-driven transaction-manager="transactionManager"/>
可以在方法上加注解
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,timeout = -1,readOnly = false,rollbackFor ={Exception.class}) public int transferMoney(String fromName, String toName, double money) { // 账户A给账户B转钱,这里只是演示,假设账户A的钱≥要转出的钱 // 账户A Account accountA = accountMapper.selectOne(new QueryWrapper<Account>().eq("username", fromName)); accountA.setMoney(accountA.getMoney()-money); int countA = accountMapper.updateById(accountA); // int countA = accountMapper.update(accountA,new QueryWrapper<Account>().eq("username",fromName)); // 模拟异常 int i=1/0; // 账户B Account accountB = accountMapper.selectOne(new QueryWrapper<Account>().eq("username", toName)); accountB.setMoney(accountB.getMoney()+money); int countB = accountMapper.updateById(accountB); // int countB = accountMapper.update(accountB,new QueryWrapper<Account>().eq("username",toName)); return countA+countB; }
测试异常
自动回滚了
也可以在类上加注解,表示全局,而方法上加注解表示局部,如果全局和局部都有注解,那么方法上的注解优先。
原文会持续更新,原文地址:https://www.cnblogs.com/uncleyong/p/17035170.html
标签:username,事务管理,money,qzcsbj,Spring,import,com,public From: https://www.cnblogs.com/uncleyong/p/17035170.html