Spring事务

目录

手动操作

 声明式提交

 注解的属性

 事务隔离级别

事务传播机制


事务可以将一组操作封装成为一个单元,一组操作要么全部成功,要么全部失败

Mysql中操作事务,有三个步骤;

1、start transaction ;开启事务

2、commit;提交事务

3、rollback;回滚事务

Spring操作事务,可以分为手动操作和声明式自动提交

手动操作

SpringBoot内置了两个对象,DataSourceTransactionManager用户获取事务、开启事务、,提交事务、回滚事务。TransactionDefinition是事务的属性


@Controller
@RequestMapping("/user")
public class InsertUserController {

    @Autowired
    InsertUserService insertUserService;

    //用于管理事务
    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;

    private TransactionDefinition transactionDefinition;
    @RequestMapping("/insert")
    @ResponseBody
    public int insert(String name, String password) {

        //开启一个事务
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
        //插入数据库
        int i = insertUserService.insert(name, password);
        //提交事务
        dataSourceTransactionManager.commit(transactionStatus);
        //回滚事务
        //  dataSourceTransactionManager.rollback(transactionStatus);
        return i;
    }
}

在启用事务提交后,插入数据成功

 如果启用事务回滚,可以看到返回的是1,但是数据库查找不到这个数据(因为事务回滚,删除了新增的数据)

 声明式提交

使用@Transactional注解声明方法,这个方法就可以自动开启事务,自动提交事务,遇到没有处理的异常,就可以自动回滚事务

@Transactional注解通过AOP实现,也就是基于动态代理实现。在执行业务代码之前,调用代理类的方法,开启事务,如果执行成功,提交事务,否则回滚事务

@Transactional可以用于修饰类或者方法

1、修饰方法时,只能用于public方法,否则不会生效

2、修饰类时,表示对注解类的所有public方法生效


@Controller
@RequestMapping("/user")
public class InsertUserAutoController {
    @Autowired
    InsertUserService insertUserService;

    @RequestMapping("/autoinsert")
    @ResponseBody
    @Transactional
    public int insert(String name, String password) {
        int i = insertUserService.insert(name, password);
      //  System.out.println(1 / 0);
        return i;
    }
}

在没有引入异常时,事务自动提交

 在引入异常之后,并且没有手动处理这个异常,那么会自动回滚事务

 调试发现,i=1,但是调试结束之后,发现数据没有插入,也就是发生了事务回滚

 如果异常被处理了,这时spring会认为我们会自己处理业务,执行回滚操作,所以不会进行事务回滚


@Controller
@RequestMapping("/user")
public class InsertUserAutoController {
    @Autowired
    InsertUserService insertUserService;

    @RequestMapping("/autoinsert")
    @ResponseBody
    @Transactional()
    public int insert(String name, String password) {
        int i = insertUserService.insert(name, password);
        try {
            System.out.println(1 / 0);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return i;
    }
}

插入成功

 这时,有两种解决办法,手动抛出异常,手动回滚事务

1、手动抛出异常


@Controller
@RequestMapping("/user")
public class InsertUserAutoController {
    @Autowired
    InsertUserService insertUserService;

    @RequestMapping("/autoinsert")
    @ResponseBody
    @Transactional()
    public int insert(String name, String password) {
        int i = insertUserService.insert(name, password);
        try {
            System.out.println(1 / 0);
        } catch (Exception e) {
            System.out.println(e.getMessage());
            throw e;//手动抛出异常
        }
        return i;
    }
}

2、手动回滚事务


@Controller
@RequestMapping("/user")
public class InsertUserAutoController {
    @Autowired
    InsertUserService insertUserService;

    @RequestMapping("/autoinsert")
    @ResponseBody
    @Transactional()
    public int insert(String name, String password) {
        int i = insertUserService.insert(name, password);
        try {
            System.out.println(1 / 0);
        } catch (Exception e) {
            System.out.println(e.getMessage());
            //手动回滚事务
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

        }
        return i;
    }
}

 注解的属性

@Transactional 注解的关键属性大致有九个:

readOnly(事务读写性)

默认情况下是 false(即:不指定只读性),设置为 true 的含义是: 告诉程序该方法下使用的是只读操作,如果进行其他非读操作,则会抛出异常。

如果一次执行多次查询,这时为了保证数据整体的一致性,要用只读事务。

noRollbackFor 和 noRollbackForClassName(遇到时不回滚)
用来指明不回滚的条件是哪些异常类或者异常类名。

rollbackFor 和 rollbackForClassName(遇到时回滚)
用来指明回滚的条件是哪些异常类或者异常类名。

timeout(超时时间)
用于设置事务处理的时间长度,阻止可能出现的长时间的阻塞系统或者占用系统资源,单位为秒,超出设置时间,会抛出异常TransactionTimedOutException

默认值是-1,表示没有设置超时时间


value(指定使用的事务管理器)
value 主要用来指定不同的事务管理器,主要用来满足在同一个系统中,存在不同的事务管理器的场景需要。

 事务隔离级别

事务的隔离级别,可以让多个并发运行的事务更加稳定,减少并发问题

在mysql中,有四种隔离级别:读未提交,读已提交,可重复读,可串行化

而在spring中,有五种隔离级别,由isolation属性设置

Isolation.DEFAULT:使用连接数据库中默认的隔离级别【默认】
Isolation.READ_UNCOMMITTED:读取未提交数据
Isolation.READ_COMMITTED:读取已提交数据
Isolation.REPEATABLE_READ:可重复读
Isolation.SERIALIZABLE:串行化

 1、当Spring中设置的隔离级别和数据库的隔离级别有区别时,以Spring中设置的隔离级别为准

2、Spring中隔离级别的实现以连接的数据库的隔离级别的支持为基础

事务传播机制

事务传播机制表示了多个包含了事务的方法在相互调用时,事务在这些方法中如何传递

事务隔离级别用于保证多个事务并发执行的稳定性;事务传播机制用于保证一个事务在多个调用方法之间的稳定性

Spring中定义了“七个”表示隔离级别的值,由propagation属性设置

1、Propagation.REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务,加入这个事务。是Spring默认的传播机制
2、Propagation.SUPPORTS:如果当前有事务,就以事务方式执行;如果当前没有事务,就以非事务方式执行
3、Propagation.MANDATORY:如果已经存在一个事务,加入这个事务。如果当前不存在事务,抛出异常
4、Propagation.REQUIRES_NEW:不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
5、Propagation.NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,就把当前事务挂起,非事务方式的方法完毕,继续执行挂起的事务
6、Propagation.NEVER:以非事务方式执行,如果当前存在事务,则抛出异常
7、Propagation.NESTED:如果当前存在事务,则创建一个新的事务作为这个事务的嵌套事务来运行;如果当前没有事务,则创建一个新的事务

1、默认级别(Propagation.REQUIRED):如果当前没有事务,就新建一个事务,如果已经存在一个事务,加入这个事务


@Controller
@RequestMapping("/user")
public class InsertUserAutoController {
    @Autowired
    InsertUserService insertUserService;
 
    @RequestMapping("/autoinsert")
    @ResponseBody
    
    public void insert(String name, String password) throws InterruptedException {

        //一个增加 user表的事务
       insertUserService.insert(name, password);
        

    }
}


@Service
public class InsertUserService {

    @Autowired
    InsertUserMapper insertUserMapper;
    @Autowired
    InsertLogService insertLogService;

    @Transactional()
    public int insert(@Param("user_name") String user_name, @Param("user_password") String user_password) {
        
        int i = insertUserMapper.insert(user_name, user_password);
        System.out.println("添加User表的数据的行数" + i);

        java.util.Date day = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        //一个增加 日志表的事务
        insertLogService.insert(user_name, sdf.format(day));
        return i;
    }
}
@Service
public class InsertLogService {
    @Autowired
    InsertBlogMapper insertBlogMapper;

    @Transactional()
    public int insert(String name, String time) {

        int i = insertBlogMapper.insert(name, time);
        System.out.println("添加日志表的数据的行数" + i);
        System.out.println(1 / 0);
        return i;
    }
}

程序执行的过程:调用InsertUserAutoController类-》调用user表的数据新增方法-》调用log表的数据新增方法

因为采用默认的传播机制,InsertUserService类中创建一个事务之后,调用InsertLogService类的方法,在log表新增数据,此时不会创建新的事务,而是会进入之前InsertUserService类中创建的事务,但是InsertLogService类中方法在新增数据时报错,导致事务回滚,因为是同一个事务,所以两个表的数据都会被回滚

  查询表数据,数据被回滚

2、Propagation.SUPPORTS:如果当前有事务,就以事务方式执行;如果当前没有事务,就以非事务方式执行


@Service
public class InsertLogService {
    @Autowired
    InsertBlogMapper insertBlogMapper;

    @Transactional(propagation = Propagation.SUPPORTS)
    public int insert(String name, String time) {

        int i = insertBlogMapper.insert(name, time);
        System.out.println("添加日志表的数据的行数" + i);
        System.out.println(1 / 0);
        return i;
    }
}


@Controller
@RequestMapping("/user")
public class InsertUserAutoController {
    @Autowired
    InsertLogService insertLogService;

    @RequestMapping("/autoinsert")
    @ResponseBody

    public void insert(String name, String password) throws InterruptedException {

        java.util.Date day = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        //一个增加 日志表的事务
        insertLogService.insert(name, sdf.format(day));

    }
}

程序执行的过程:调用InsertUserAutoController类-》调用log表的数据新增方法;

InsertLogService采用的传播机制是SUPPORTS,所以log表新增操作此时是以非事务的方式运行的,不会造成事务回滚

 3、Propagation.MANDATORY:如果已经存在一个事务,加入这个事务。如果当前不存在事务,抛出异常

 4、Propagation.REQUIRES_NEW:不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务


@Controller
@RequestMapping("/user")
public class InsertUserAutoController {
    @Autowired
    InsertUserService insert;
    @Autowired
    InsertLogService insertLogService;

    @RequestMapping("/autoinsert")
    @ResponseBody

    public void insert(String name, String password)  {

        //一个增加 user表的事务
        insert.insert(name, password);


        java.util.Date day = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        //一个增加 日志表的事务
        insertLogService.insert(name, sdf.format(day));
    }
}

@Service
public class InsertLogService {
    @Autowired
    InsertBlogMapper insertBlogMapper;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public int insert(String name, String time) {
        int i = insertBlogMapper.insert(name, time);
        System.out.println("添加日志表的数据的行数" + i);
        System.out.println(1/0);
        return i;
    }
}
@Service
public class InsertUserService {

    @Autowired
    InsertUserMapper insertUserMapper;


    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public int insert(@Param("user_name") String user_name, @Param("user_password") String user_password) {

        int i = insertUserMapper.insert(user_name, user_password);
        System.out.println("添加User表的数据的行数" + i);
        return i;
    }
}

程序执行的过程:调用InsertUserAutoController类-》调用user表的数据新增方法,调用log表的数据新增方法;

采用REQUIRES_NEW这个传播机制,user表新增会创建一个事务,在log表新增也会创建一个事务,此时,log表新增报错,事务回滚,但是user表不会事务回滚

 

 5、Propagation.NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,就把当前事务挂起

当InsertLogService采用NOT_SUPPORTED传播级别,而InsertUserService采用默认级别时

程序执行的过程:调用InsertUserAutoController类-》调用user表的数据新增方法-》调用log表的数据新增方法;

采用NOT_SUPPORTED传播机制,InsertUserService中会创建一个事务,在InsertLogService会挂起存在的事务,InsertLogService的方法会以非事务方式运行,运行结束后恢复InsertUserService的事务

6、Propagation.NEVER:以非事务方式执行,如果当前存在事务,则抛出异常
7、Propagation.NESTED:如果当前存在事务,则创建一个新的事务作为这个事务的嵌套事务来运行;如果当前没有事务,则创建一个新的事务


@Service
public class InsertUserService {

    @Autowired
    InsertUserMapper insertUserMapper;
    @Autowired
    InsertLogService insertLogService;

    @Transactional(propagation = Propagation.NESTED)
    public int insert(@Param("user_name") String user_name, @Param("user_password") String user_password) {

        int i = insertUserMapper.insert(user_name, user_password);
        System.out.println("添加User表的数据的行数" + i);

        java.util.Date day = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        //一个增加 日志表的事务
        insertLogService.insert(user_name, sdf.format(day));
        return i;
    }
}

@Service
public class InsertLogService {
    @Autowired
    InsertBlogMapper insertBlogMapper;

    @Transactional(propagation = Propagation.NESTED)
    public int insert(String name, String time) {
        int i = insertBlogMapper.insert(name, time);
        System.out.println("添加日志表的数据的行数" + i);
        System.out.println(1/0);
        return i;
    }
}

@Controller
@RequestMapping("/user")
public class InsertUserAutoController {
    @Autowired
    InsertUserService insertUserService;

    @RequestMapping("/autoinsert")
    @ResponseBody

    public void insert(String name, String password)  {

        //一个增加 user表的事务
        insertUserService.insert(name, password);

    }
}

程序执行的过程:调用InsertUserAutoController类-》调用user表的数据新增方法-》调用log表的数据新增方法;

InsertUserService采用NESTED传播机制,创建了一个事务,在  InsertLogService类中使用 @Transactional()创建了一个事务,这个事务会作为InsertUserService开启的事务的嵌套事务来运行

当InsertLogService事务出现异常,事务回滚,这时,会继续向上查找调用InsertLogService方法的事务,对其进行回滚,所以,两个表都不会插入数据

 

REQUIRED和NESTED两种传播机制的区别:

NESTED:如果当前存在事务,则创建一个新的事务作为这个事务的嵌套事务来运行;如果当前没有事务,则创建一个新的事务

REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务,加入这个事务

两个实际上都可以实现A,B两个事务的关联,只不过NESTED是A包含B,REQUIRED是AB一起

在NESTED中,在一个保存点,如果B事务手动回滚,会保留到B事务之前的状态,也就是不影响A事务。而REQUIRED没有这种机制。所以,NESTED可以实现部分事务回滚


@Service
public class InsertLogService {
    @Autowired
    InsertBlogMapper insertBlogMapper;

    @Transactional(propagation = Propagation.NESTED)
    public int insert(String name, String time) {
        int i = insertBlogMapper.insert(name, time);
        System.out.println("添加日志表的数据的行数" + i);
        try {
            System.out.println(1 / 0);
        } catch (Exception e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return i;
    }
}

 insertUserService类和insertLogService类的事务传播级别都是NESTED,而insertLogService中实现了手动回滚,回到了保留点,不会影响insertUserService的事务

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值