17. Spring 事务

目录

1. 事务定义

2. MySQL 中的事务使用

3. 没有事务时的插入

4. Spring 编程式事务

5. Spring 声明式事务

5.1  @Transactional 作用范围

5.2  @Transactional 参数说明

5.3  @Transactional 工作原理


1. 事务定义

将⼀组操作封装成一个执行单元(封装到一起),要么全部成功,要么全部失败。
为什么要用事务?
比如转账分为两个操作:
第一步操作:A -100
第二步操作:A+100
果没有事务,第一步执行成功了,第二步执行失败了,那么 A 账户平高白无故的 100 元就没有 了。而如果使用事务就可以解决这个问题,让这⼀组操作要么一起成功,要么一起失败。

2. MySQL 中的事务使用

事务在 MySQL 有 3 个重要的操作:开启事务、提交事务、回滚事务,它们对应的操作命令如下:
--开启事务
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;
    }
}



    
        insert into userinfo (username,password,photo)values(#{username},#{password},#{photo})
@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);
    }
}

 在运行前查看数据库的数据如下图所示:

17. Spring 事务_第1张图片

 运行以上代码后可以看到:

17. Spring 事务_第2张图片

此时可以看到数据成功插入: 

17. Spring 事务_第3张图片

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: 

17. Spring 事务_第4张图片

17. Spring 事务_第5张图片 但是,此时可以看到数据库中的数据并未添加:

17. Spring 事务_第6张图片

这是因为事务进行了回滚。接下来我们看一下事务的提交

@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;
    }
}

可以看到数据此时提交成功:

17. Spring 事务_第7张图片

17. Spring 事务_第8张图片

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;
    }
}

17. Spring 事务_第9张图片

 根据打印的日志我们可以看到数据提交成功了:

17. Spring 事务_第10张图片

17. Spring 事务_第11张图片

那么,我们如何让事务进行回滚呢?

手动添加异常:

@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;
    }
}

 运行结果:17. Spring 事务_第12张图片

 可以看到数据并没有提交:

17. Spring 事务_第13张图片

 查看日志可以发现,此时打印的日志和事务回滚时的日志相同:

17. Spring 事务_第14张图片

此时,我们去掉注解 @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;
    }
}

17. Spring 事务_第15张图片

17. Spring 事务_第16张图片

也就是说,在没有注解 @Transactional 时,数据是可以提交成功的;添加注解 @Transactional,当有异常时,事务会进行回滚。

通过注解,不需要我们手动开启事务和关闭事务,如果程序执行成功,自动提交事务;如果程序执行异常,自动回滚事务。

5.1  @Transactional 作用范围

@Transactional 可以用来修饰方法或类:

  • 修饰方法:只能应用到 public 方法上,否则不生效
  • 修饰类:表明该注解对该类中所有的 public 方法都生效

5.2  @Transactional 参数说明

17. Spring 事务_第17张图片

接下来,我们设置在发生算数异常时,不进行回滚:

@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;
    }
}

17. Spring 事务_第18张图片

17. Spring 事务_第19张图片

 接下来我们手动扔出异常:

@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();
    }
}

17. Spring 事务_第20张图片

17. Spring 事务_第21张图片

17. Spring 事务_第22张图片

 可以看到并没有进行回滚。

@Transactional 默认只在遇到运行时异常和Error时才会回滚,非运行时异常不回滚,即 Exception 的子类中,除了 RuntimeException 及其子类。

17. Spring 事务_第23张图片

显式的指定所有异常均需回滚:

@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();
    }
}

17. Spring 事务_第24张图片

可以看到事务进行了回滚:

17. Spring 事务_第25张图片

17. Spring 事务_第26张图片

 如果异常被捕获,事务不会回滚:

@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;
    }
}

17. Spring 事务_第27张图片

可以看到事务没有回滚,正常提交: 

17. Spring 事务_第28张图片

17. Spring 事务_第29张图片

5.3  @Transactional 工作原理

@Transactional 是基于 AOP 实现的,AOP 又是使用动态代理实现的。如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理,如果目标对象没有实现了接口,会使用 CGLIB 动态代理。
@Transactional 在开始执行业务之前,通过代理先开启事务,在执行成功之后再提交事务。如果中途到异常,则回滚事务。

你可能感兴趣的:(java,ee,spring,java,数据库)