目录
1. 事务定义
2. MySQL 中的事务使用
3. 没有事务时的插入
4. Spring 编程式事务
5. Spring 声明式事务
5.1 @Transactional 作用范围
5.2 @Transactional 参数说明
5.3 @Transactional 工作原理
1. 事务定义
比如转账分为两个操作:第一步操作:A -100第二步操作:A+100
2. MySQL 中的事务使用
--开启事务
start transaction;
--业务执行
--提交事务
commit;
--回滚事务
rollback;
3. 没有事务时的插入
@Mapper
public interface UserMapper {
// 插入数据
Integer insert(User user);
}
@Data
public class User {
private Integer id;
private String username;
private String password;
private String photo;
private Date createtime;
private Date updatetime;
public User(){
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
}
<?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.example.demo.mapper.UserMapper">
<insert id="insert">
insert into userinfo (username,password,photo)values(#{username},#{password},#{photo})
</mapper>
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public Integer insert(User user){
return userMapper.insert(user);
}
}
@RequestMapping("/trans")
@RestController
public class TransactionalController {
@Autowired
private UserService userService;
@RequestMapping("/addUser")
public Integer addUser(String username,String password){
User user = new User(username,password);
return userService.insert(user);
}
}
在运行前查看数据库的数据如下图所示:
运行以上代码后可以看到:
此时可以看到数据成功插入:
4. Spring 编程式事务
@Slf4j
@RequestMapping("/trans")
@RestController
public class TransactionalController {
@Autowired
private UserService userService;
// 获取数据库事务管理器
@Autowired
private DataSourceTransactionManager dataSourceTransactionManager;
private TransactionDefinition transactionDefinition;
@RequestMapping("/addUser")
public Integer addUser(String username,String password){
TransactionStatus transaction = dataSourceTransactionManager.getTransaction(transactionDefinition);
User user = new User(username,password);
Integer result = userService.insert(user);
log.info("影响行数:" +result);
// 事务回滚
dataSourceTransactionManager.rollback(transaction);
return result;
}
}
运行成功,返回1:
但是,此时可以看到数据库中的数据并未添加:
这是因为事务进行了回滚。接下来我们看一下事务的提交:
@Slf4j
@RequestMapping("/trans")
@RestController
public class TransactionalController {
@Autowired
private UserService userService;
// 获取数据库事务管理器
@Autowired
private DataSourceTransactionManager dataSourceTransactionManager;
@Autowired
private TransactionDefinition transactionDefinition;
@RequestMapping("/addUser")
public Integer addUser(String username,String password){
TransactionStatus transaction = dataSourceTransactionManager.getTransaction(transactionDefinition);
User user = new User(username,password);
Integer result = userService.insert(user);
log.info("影响行数:" +result);
// 事务回滚
// dataSourceTransactionManager.rollback(transaction);
// 提交事务
dataSourceTransactionManager.commit(transaction);
return result;
}
}
可以看到数据此时提交成功:
5. Spring 声明式事务
@Slf4j
@RequestMapping("/trans2")
@RestController
public class TransactionalController2 {
@Autowired
private UserService userService;
@Transactional
@RequestMapping("/addUser")
public Integer addUser(String username,String password){
User user = new User(username,password);
Integer result = userService.insert(user);
log.info("影响行数:"+result);
return result;
}
}
根据打印的日志我们可以看到数据提交成功了:
那么,我们如何让事务进行回滚呢?
手动添加异常:
@Slf4j
@RequestMapping("/trans2")
@RestController
public class TransactionalController2 {
@Autowired
private UserService userService;
@Transactional
@RequestMapping("/addUser")
public Integer addUser(String username,String password){
User user = new User(username,password);
Integer result = userService.insert(user);
log.info("影响行数:"+result);
int a = 10/0;
return result;
}
}
运行结果:
可以看到数据并没有提交:
查看日志可以发现,此时打印的日志和事务回滚时的日志相同:
此时,我们去掉注解 @Transactional:
@Slf4j
@RequestMapping("/trans2")
@RestController
public class TransactionalController2 {
@Autowired
private UserService userService;
// @Transactional
@RequestMapping("/addUser")
public Integer addUser(String username,String password){
User user = new User(username,password);
Integer result = userService.insert(user);
log.info("影响行数:"+result);
int a = 10/0;
return result;
}
}
也就是说,在没有注解 @Transactional 时,数据是可以提交成功的;添加注解 @Transactional,当有异常时,事务会进行回滚。
通过注解,不需要我们手动开启事务和关闭事务,如果程序执行成功,自动提交事务;如果程序执行异常,自动回滚事务。
5.1 @Transactional 作用范围
@Transactional 可以用来修饰方法或类:
- 修饰方法:只能应用到 public 方法上,否则不生效
- 修饰类:表明该注解对该类中所有的 public 方法都生效
5.2 @Transactional 参数说明
接下来,我们设置在发生算数异常时,不进行回滚:
@Slf4j
@RequestMapping("/trans2")
@RestController
public class TransactionalController2 {
@Autowired
private UserService userService;
@Transactional(noRollbackFor = ArithmeticException.class)
@RequestMapping("/addUser")
public Integer addUser(String username,String password){
User user = new User(username,password);
Integer result = userService.insert(user);
log.info("影响行数:"+result);
int a = 10/0;
return result;
}
}
接下来我们手动扔出异常:
@Slf4j
@RequestMapping("/trans2")
@RestController
public class TransactionalController2 {
@Autowired
private UserService userService;
/**
* 指定异常回滚
* @param username
* @param password
* @return
*/
@Transactional
@RequestMapping("/addUser2")
public Integer addUser2(String username,String password) throws Exception {
User user = new User(username,password);
Integer result = userService.insert(user);
log.info("影响行数:"+result);
throwException();
return result;
}
public void throwException() throws Exception{
throw new IOException();
}
}
可以看到并没有进行回滚。
@Transactional 默认只在遇到运行时异常和Error时才会回滚,非运行时异常不回滚,即 Exception 的子类中,除了 RuntimeException 及其子类。
显式的指定所有异常均需回滚:
@Slf4j
@RequestMapping("/trans2")
@RestController
public class TransactionalController2 {
@Autowired
private UserService userService;
/**
* 指定异常回滚
* @param username
* @param password
* @return
*/
@Transactional(rollbackFor = Exception.class)
// 显式的指定所有异常均需要回滚
@RequestMapping("/addUser2")
public Integer addUser2(String username,String password) throws Exception {
User user = new User(username,password);
Integer result = userService.insert(user);
log.info("影响行数:"+result);
throwException();
return result;
}
public void throwException() throws Exception{
throw new IOException();
}
}
可以看到事务进行了回滚:
如果异常被捕获,事务不会回滚:
@Slf4j
@RequestMapping("/trans2")
@RestController
public class TransactionalController2 {
@Autowired
private UserService userService;
@Transactional
@RequestMapping("/addUser3")
public Integer addUser3(String username,String password) throws Exception {
User user = new User(username,password);
Integer result = userService.insert(user);
log.info("影响行数:"+result);
try{
int a = 10/0;
}catch (Exception e){
e.printStackTrace();
}
return result;
}
}
可以看到事务没有回滚,正常提交: