精华内容
下载资源
问答
  • # 引言昨天公众号粉丝咨询了一个问题,说自己之前面试被问@Transactional注解哪些场景下会失效,一时语塞致使面试失败。所以今天简单和大家分享一下@...使用@Transactional注解时需要注意许多细节,...

    6a6410eef3aac0b84fa918ef7f815446.png

    # 引言

    昨天公众号粉丝咨询了一个问题,说自己之前面试被问@Transactional注解哪些场景下会失效,一时语塞致使面试失败。所以今天简单的和大家分享一下

    @Transactional相关的知识。

    @Transactional 注解相信大家并不陌生,平时开发中很常用的一个注解,它能保证方法内多个数据库操作要么同时成功、要么同时失败。使用@Transactional注解时需要注意许多的细节,不然你会发现@Transactional总是莫名其妙的就失效了。

    一、事务

    事务管理在系统开发中是不可缺少的一部分,Spring提供了很好事务管理机制,主要分为编程式事务和声明式事务两种。

    编程式事务:是指在代码中手动的管理事务的提交、回滚等操作,代码侵入性比较强,如下示例:

    try {    //TODO something     transactionManager.commit(status);} catch (Exception e) {    transactionManager.rollback(status);    throw new InvoiceApplyException("异常失败");

    声明式事务:基于AOP面向切面的,它将具体业务与事务处理部分解耦,代码侵入性很低,所以在实际开发中声明式事务用的比较多。声明式事务也有两种实现方式,一是基于TX和AOP的xml配置文件方式,二种就是基于@Transactional注解了。

    @Transactional    @GetMapping("/test")    public String test() {            int insert = cityInfoDictMapper.insert(cityInfoDict);    }

    二、@Transactional介绍

    1、@Transactional注解可以作用于哪些地方?

    @Transactional 可以作用在接口、类、类方法。

    作用于类:当把@Transactional 注解放在类上时,表示所有该类的public方法都配置相同的事务属性信息。

    作用于方法:当类配置了@Transactional,方法也配置了@Transactional,方法的事务会覆盖类的事务配置信息。

    作用于接口:不推荐这种使用方法,因为一旦标注在Interface上并且配置了Spring AOP 使用CGLib动态代理,将会导致@Transactional注解失效

    @Transactional@RestController@RequestMappingpublic class MybatisPlusController {    @Autowired    private CityInfoDictMapper cityInfoDictMapper;        @Transactional(rollbackFor = Exception.class)    @GetMapping("/test")    public String test() throws Exception {        CityInfoDict cityInfoDict = new CityInfoDict();        cityInfoDict.setParentCityId(2);        cityInfoDict.setCityName("2");        cityInfoDict.setCityLevel("2");        cityInfoDict.setCityCode("2");        int insert = cityInfoDictMapper.insert(cityInfoDict);        return insert + "";    }}

    2、@Transactional注有哪些属性?

    propagation属性

    propagation 代表事务的传播行为,默认值为 Propagation.REQUIRED,其他的属性信息如下:

    Propagation.REQUIRED:如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。( 也就是说如果A方法和B方法都添加了注解,在默认传播模式下,A方法内部调用B方法,会把两个方法的事务合并为一个事务 )Propagation.SUPPORTS:如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。Propagation.MANDATORY:如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。Propagation.REQUIRES_NEW:重新创建一个新的事务,如果当前存在事务,暂停当前的事务。( 当类A中的 a 方法用默认Propagation.REQUIRED模式,类B中的 b方法加上采用 Propagation.REQUIRES_NEW模式,然后在 a 方法中调用 b方法操作数据库,然而 a方法抛出异常后,b方法并没有进行回滚,因为Propagation.REQUIRES_NEW会暂停 a方法的事务 )Propagation.NOT_SUPPORTED:以非事务的方式运行,如果当前存在事务,暂停当前的事务。Propagation.NEVER:以非事务的方式运行,如果当前存在事务,则抛出异常。Propagation.NESTED :和 Propagation.REQUIRED 效果一样。

    isolation 属性

    isolation :事务的隔离级别,默认值为 Isolation.DEFAULT。

    Isolation.DEFAULT:使用底层数据库默认的隔离级别。

    Isolation.READ_UNCOMMITTED

    Isolation.READ_COMMITTED

    Isolation.REPEATABLE_READ

    Isolation.SERIALIZABLE

    timeout 属性

    timeout :事务的超时时间,默认值为 -1。如果超过该时间限制但事务还没有完成,则自动回滚事务。

    readOnly 属性

    readOnly :指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。

    rollbackFor 属性

    rollbackFor :用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。

    noRollbackFor属性**

    noRollbackFor:抛出指定的异常类型,不回滚事务,也可以指定多个异常类型。

    二、@Transactional失效场景

    接下来我们结合具体的代码分析一下哪些场景下,@Transactional 注解会失效。

    1、@Transactional 应用在非 public 修饰的方法上

    如果Transactional注解应用在非public 修饰的方法上,Transactional将会失效。

    9d38e887a5e542691157f3bc7b2180c6.png

    之所以会失效是因为在Spring AOP 代理时,如上图所示 TransactionInterceptor (事务拦截器)在目标方法执行前后进行拦截,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 
    AbstractFallbackTransactionAttributeSource的 
    computeTransactionAttribute 方法,获取Transactional 注解的事务配置信息。

    protected TransactionAttribute computeTransactionAttribute(Method method,    Class> targetClass) {        // Don't allow no-public methods as required.        if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {        return null;}

    此方法会检查目标方法的修饰符是否为 public,不是 public则不会获取@Transactional 的属性配置信息。

    注意:protected、private 修饰的方法上使用 @Transactional 注解,虽然事务无效,但不会有任何报错,这是我们很容犯错的一点。

    2、@Transactional 注解属性 propagation 设置错误

    这种失效是由于配置错误,若是错误的配置以下三种 propagation,事务将不会发生回滚。

    TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。 


    TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。 关注公众号小黄鸭编程社区,回复关键字手册,获取最新开发手册


    TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。

    3、@Transactional 注解属性 rollbackFor 设置错误

    rollbackFor 可以指定能够触发事务回滚的异常类型。Spring默认抛出了未检查unchecked异常(继承自 RuntimeException 的异常)或者 Error才回滚事务;其他异常不会触发回滚事务。如果在事务中抛出其他类型的异常,但却期望 Spring 能够回滚事务,就需要指定rollbackFor属性。

    e58e4b939d848f9e42049b3333039ae3.png

    // 希望自定义的异常可以进行回滚@Transactional(propagation= Propagation.REQUIRED,rollbackFor= MyException.class
    若在目标方法中抛出的异常是 rollbackFor 指定的异常的子类,事务同样会回滚。Spring源码如下:
    private int getDepth(Class> exceptionClass, int depth) {        if (exceptionClass.getName().contains(this.exceptionName)) {            // Found it!            return depth;}        // If we've gone as far as we can go and haven't found it...        if (exceptionClass == Throwable.class) {            return -1;}return getDepth(exceptionClass.getSuperclass(), depth + 1);}

    4、同一个类中方法调用,导致@Transactional失效

    开发中避免不了会对同一个类里面的方法调用,比如有一个类Test,它的一个方法A,A再调用本类的方法B(不论方法B是用public还是private修饰),但方法A没有声明注解事务,而B方法有。则外部调用方法A之后,方法B的事务是不会起作用的。这也是经常犯错误的一个地方。

    那为啥会出现这种情况?其实这还是由于使用Spring AOP代理造成的,因为只有当事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理。

    //@Transactional    @GetMapping("/test")    private Integer A() throws Exception {        CityInfoDict cityInfoDict = new CityInfoDict();        cityInfoDict.setCityName("2");        /**         * B 插入字段为 3的数据         */        this.insertB();        /**         * A 插入字段为 2的数据         */        int insert = cityInfoDictMapper.insert(cityInfoDict);        return insert;    }    @Transactional()    public Integer insertB() throws Exception {        CityInfoDict cityInfoDict = new CityInfoDict();        cityInfoDict.setCityName("3");        cityInfoDict.setParentCityId(3);        return cityInfoDictMapper.insert(cityInfoDict);    }

    5、异常被你的 catch“吃了”导致@Transactional失效

    这种情况是最常见的一种@Transactional注解失效场景,

    @Transactional    private Integer A() throws Exception {        int insert = 0;        try {            CityInfoDict cityInfoDict = new CityInfoDict();            cityInfoDict.setCityName("2");            cityInfoDict.setParentCityId(2);            /**             * A 插入字段为 2的数据             */            insert = cityInfoDictMapper.insert(cityInfoDict);            /**             * B 插入字段为 3的数据             */            b.insertB();        } catch (Exception e) {            e.printStackTrace();        }    }

    如果B方法内部抛了异常,而A方法此时try catch了B方法的异常,那这个事务还能正常回滚吗?

    答案:不能!

    会抛出异常:

    org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
    因为当ServiceB中抛出了一个异常以后,ServiceB标识当前事务需要rollback。但是ServiceA中由于你手动的捕获这个异常并进行处理,ServiceA认为当前事务应该正常commit。此时就出现了前后不一致,也就是因为这样,抛出了前面的
    UnexpectedRollbackException异常。

    spring的事务是在调用业务方法之前开始的,业务方法执行完毕之后才执行commit or rollback,事务是否执行取决于是否抛出runtime异常。如果抛出runtime exception 并在你的业务方法中没有catch到的话,事务会回滚。

    在业务方法中一般不需要catch异常,如果非要catch一定要抛出throw new RuntimeException(),或者注解中指定抛异常类型@Transactional(rollbackFor=Exception.class),否则会导致事务失效,数据commit造成数据不一致,所以有些时候try catch反倒会画蛇添足。

    6、数据库引擎不支持事务

    这种情况出现的概率并不高,事务能否生效数据库引擎是否支持事务是关键。常用的MySQL数据库默认使用支持事务的innodb引擎。一旦数据库引擎切换成不支持事务的myisam,那事务就从根本上失效了。

    # 总结

    @Transactional 注解的看似简单易用,但如果对它的用法一知半解,还是会踩到很多坑的

    作者:程序员内点事

    来源:juejin.im/post/5e72e97c6fb9a07cb346083f

     往期推荐 

    ?

    • 我的VS Code设置,高效编码!
    • 这12张手绘图,让我彻底搞懂了微服务架构!
    • 12个让您震撼的Linux终端命令

    a81b8106886a41cb6b78f0361d2927d9.png

    6af7caff2d753fa5fd739de3c56b402c.gif
    展开全文
  • 引言昨天公众号粉丝咨询了一个问题,说自己之前面试被问@Transactional注解哪些场景下会失效,一时语塞致使面试失败。所以今天简单和大家分享一下@...使用@Transactional注解时需要注意许多细节,不然你会发现@...

    引言

    昨天公众号粉丝咨询了一个问题,说自己之前面试被问@Transactional注解哪些场景下会失效,一时语塞致使面试失败。所以今天简单的和大家分享一下@Transactional相关的知识。

    @Transactional 注解相信大家并不陌生,平时开发中很常用的一个注解,它能保证方法内多个数据库操作要么同时成功、要么同时失败。使用@Transactional注解时需要注意许多的细节,不然你会发现@Transactional总是莫名其妙的就失效了。

    一、事务

    事务管理在系统开发中是不可缺少的一部分,Spring提供了很好事务管理机制,主要分为编程式事务和声明式事务两种。

    编程式事务:是指在代码中手动的管理事务的提交、回滚等操作,代码侵入性比较强,如下示例:

    1try {2    //TODO something3     transactionManager.commit(status);4} catch (Exception e) {5    transactionManager.rollback(status);6    throw new InvoiceApplyException("异常失败");7}

    声明式事务:基于AOP面向切面的,它将具体业务与事务处理部分解耦,代码侵入性很低,所以在实际开发中声明式事务用的比较多。声明式事务也有两种实现方式,一是基于TX和AOP的xml配置文件方式,二种就是基于@Transactional注解了。

    1    @Transactional2    @GetMapping("/test")3    public String test() {45        int insert = cityInfoDictMapper.insert(cityInfoDict);6    }

    二、@Transactional介绍

    1、@Transactional注解可以作用于哪些地方?

    @Transactional 可以作用在接口、类、类方法。

    • 作用于类:当把@Transactional 注解放在类上时,表示所有该类的public方法都配置相同的事务属性信息。
    • 作用于方法:当类配置了@Transactional,方法也配置了@Transactional,方法的事务会覆盖类的事务配置信息。
    • 作用于接口:不推荐这种使用方法,因为一旦标注在Interface上并且配置了Spring AOP 使用CGLib动态代理,将会导致@Transactional注解失效
     1@Transactional 2@RestController 3@RequestMapping 4public class MybatisPlusController { 5    @Autowired 6    private CityInfoDictMapper cityInfoDictMapper; 7 8    @Transactional(rollbackFor = Exception.class) 9    @GetMapping("/test")10    public String test() throws Exception {11        CityInfoDict cityInfoDict = new CityInfoDict();12        cityInfoDict.setParentCityId(2);13        cityInfoDict.setCityName("2");14        cityInfoDict.setCityLevel("2");15        cityInfoDict.setCityCode("2");16        int insert = cityInfoDictMapper.insert(cityInfoDict);17        return insert + "";18    }19}

    2、@Transactional注有哪些属性?

    propagation属性

    propagation 代表事务的传播行为,默认值为 Propagation.REQUIRED,其他的属性信息如下:

    • Propagation.REQUIRED:如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。( 也就是说如果A方法和B方法都添加了注解,在默认传播模式下,A方法内部调用B方法,会把两个方法的事务合并为一个事务 )
    • Propagation.SUPPORTS:如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。
    • Propagation.MANDATORY:如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。
    • Propagation.REQUIRES_NEW:重新创建一个新的事务,如果当前存在事务,暂停当前的事务。( 当类A中的 a 方法用默认Propagation.REQUIRED模式,类B中的 b方法加上采用 Propagation.REQUIRES_NEW模式,然后在 a 方法中调用 b方法操作数据库,然而 a方法抛出异常后,b方法并没有进行回滚,因为Propagation.REQUIRES_NEW会暂停 a方法的事务 )
    • Propagation.NOT_SUPPORTED:以非事务的方式运行,如果当前存在事务,暂停当前的事务。
    • Propagation.NEVER:以非事务的方式运行,如果当前存在事务,则抛出异常。
    • Propagation.NESTED :和 Propagation.REQUIRED 效果一样。

    isolation 属性

    isolation :事务的隔离级别,默认值为 Isolation.DEFAULT。

    • Isolation.DEFAULT:使用底层数据库默认的隔离级别。
    • Isolation.READ_UNCOMMITTED
    • Isolation.READ_COMMITTED
    • Isolation.REPEATABLE_READ
    • Isolation.SERIALIZABLE

    timeout 属性

    timeout :事务的超时时间,默认值为 -1。如果超过该时间限制但事务还没有完成,则自动回滚事务。

    readOnly 属性

    readOnly :指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。

    rollbackFor 属性

    rollbackFor :用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。

    noRollbackFor属性**

    noRollbackFor:抛出指定的异常类型,不回滚事务,也可以指定多个异常类型。

    二、@Transactional失效场景

    接下来我们结合具体的代码分析一下哪些场景下,@Transactional 注解会失效。

    1、@Transactional 应用在非 public 修饰的方法上

    如果Transactional注解应用在非public 修饰的方法上,Transactional将会失效。

    19715f86292b4d63c3e7599f95f9495e.png

    在这里插入图片描述

    之所以会失效是因为在Spring AOP 代理时,如上图所示 TransactionInterceptor (事务拦截器)在目标方法执行前后进行拦截,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource的 computeTransactionAttribute 方法,获取Transactional 注解的事务配置信息。

    1protected TransactionAttribute computeTransactionAttribute(Method method,2    Class> targetClass) {3        // Don't allow no-public methods as required.4        if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {5        return null;6}

    此方法会检查目标方法的修饰符是否为 public,不是 public则不会获取@Transactional 的属性配置信息。

    注意:protected、private 修饰的方法上使用 @Transactional 注解,虽然事务无效,但不会有任何报错,这是我们很容犯错的一点。

    2、@Transactional 注解属性 propagation 设置错误

    这种失效是由于配置错误,若是错误的配置以下三种 propagation,事务将不会发生回滚。

    TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。

    3、@Transactional 注解属性 rollbackFor 设置错误

    rollbackFor 可以指定能够触发事务回滚的异常类型。Spring默认抛出了未检查unchecked异常(继承自 RuntimeException的异常)或者 Error才回滚事务;其他异常不会触发回滚事务。如果在事务中抛出其他类型的异常,但却期望 Spring 能够回滚事务,就需要指定 rollbackFor属性。

    af1df603a146b3c70c8afb8f05a38ad2.png

    在这里插入图片描述

    1// 希望自定义的异常可以进行回滚2@Transactional(propagation= Propagation.REQUIRED,rollbackFor= MyException.class

    若在目标方法中抛出的异常是 rollbackFor 指定的异常的子类,事务同样会回滚。Spring源码如下:

     1private int getDepth(Class> exceptionClass, int depth) { 2        if (exceptionClass.getName().contains(this.exceptionName)) { 3            // Found it! 4            return depth; 5} 6        // If we've gone as far as we can go and haven't found it... 7        if (exceptionClass == Throwable.class) { 8            return -1; 9}10return getDepth(exceptionClass.getSuperclass(), depth + 1);11}

    4、同一个类中方法调用,导致@Transactional失效

    开发中避免不了会对同一个类里面的方法调用,比如有一个类Test,它的一个方法A,A再调用本类的方法B(不论方法B是用public还是private修饰),但方法A没有声明注解事务,而B方法有。则外部调用方法A之后,方法B的事务是不会起作用的。这也是经常犯错误的一个地方。

    那为啥会出现这种情况?其实这还是由于使用Spring AOP代理造成的,因为只有当事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理。

     1//@Transactional 2    @GetMapping("/test") 3    private Integer A() throws Exception { 4        CityInfoDict cityInfoDict = new CityInfoDict(); 5        cityInfoDict.setCityName("2"); 6        /** 7         * B 插入字段为 3的数据 8         */ 9        this.insertB();10        /**11         * A 插入字段为 2的数据12         */13        int insert = cityInfoDictMapper.insert(cityInfoDict);1415        return insert;16    }1718    @Transactional()19    public Integer insertB() throws Exception {20        CityInfoDict cityInfoDict = new CityInfoDict();21        cityInfoDict.setCityName("3");22        cityInfoDict.setParentCityId(3);2324        return cityInfoDictMapper.insert(cityInfoDict);25    }

    5、异常被你的 catch“吃了”导致@Transactional失效

    这种情况是最常见的一种@Transactional注解失效场景,

     1    @Transactional 2    private Integer A() throws Exception { 3        int insert = 0; 4        try { 5            CityInfoDict cityInfoDict = new CityInfoDict(); 6            cityInfoDict.setCityName("2"); 7            cityInfoDict.setParentCityId(2); 8            /** 9             * A 插入字段为 2的数据10             */11            insert = cityInfoDictMapper.insert(cityInfoDict);12            /**13             * B 插入字段为 3的数据14             */15            b.insertB();16        } catch (Exception e) {17            e.printStackTrace();18        }19    }

    如果B方法内部抛了异常,而A方法此时try catch了B方法的异常,那这个事务还能正常回滚吗?

    答案:不能!

    会抛出异常:

    1org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

    因为当ServiceB中抛出了一个异常以后,ServiceB标识当前事务需要rollback。但是ServiceA中由于你手动的捕获这个异常并进行处理,ServiceA认为当前事务应该正常commit。此时就出现了前后不一致,也就是因为这样,抛出了前面的UnexpectedRollbackException异常。

    spring的事务是在调用业务方法之前开始的,业务方法执行完毕之后才执行commit or rollback,事务是否执行取决于是否抛出runtime异常。如果抛出runtime exception 并在你的业务方法中没有catch到的话,事务会回滚。

    在业务方法中一般不需要catch异常,如果非要catch一定要抛出throw new RuntimeException(),或者注解中指定抛异常类型@Transactional(rollbackFor=Exception.class),否则会导致事务失效,数据commit造成数据不一致,所以有些时候try catch反倒会画蛇添足。

    6、数据库引擎不支持事务

    这种情况出现的概率并不高,事务能否生效数据库引擎是否支持事务是关键。常用的MySQL数据库默认使用支持事务的innodb引擎。一旦数据库引擎切换成不支持事务的myisam,那事务就从根本上失效了。

    总结

    @Transactional 注解的看似简单易用,但如果对它的用法一知半解,还是会踩到很多坑的。


    今天就说这么多,如果本文对您有一点帮助,希望能得到您一个点赞哦

    您的认可才是我写作的动力!

    小福利:

    评论【666

    188a243b492d45ab98ecf8d5a48cbc35.png
    展开全文
  • transaction是我们在做...本文我们将会深入的探讨Spring Boot JPA中@Transactional注解的使用。通过@Transactional注解,我们可以设置事物的传播级别和隔离级别,同时可以设置timeout, read-only, 和 rollback等特性...

    transaction是我们在做数据库操作的时候不能回避的一个话题,通过transaction,我们可以保证数据库操作的原子性,一致性,隔离性和持久性。

    本文我们将会深入的探讨Spring Boot JPA中@Transactional注解的使用。

    通过@Transactional注解,我们可以设置事物的传播级别和隔离级别,同时可以设置timeout, read-only, 和 rollback等特性。

    @Transactional的实现

    Spring通过创建代理或者操纵字节码来实现事物的创建,提交和回滚操作。如果是代理模式的话,Spring会忽略掉@Transactional的内部方法调用。

    如果我们有个方法callMethod,并标记它为@Transactional,那么Spring Boot的实现可能是如下方式:

    createTransactionIfNecessary();try {    callMethod();    commitTransactionAfterReturning();} catch (exception) {    completeTransactionAfterThrowing();    throw exception;}

    @Transactional的使用

    @Transactional使用起来很简单,可以放在class上,可以放在interface上,也可以放在方法上面。

    如果放在方法上面,那么该方法中的所有public方法都会应用该Transaction。

    如果@Transactional放在private方法上面,则Spring Boot将会忽略它。

    Transaction的传播级别

    传播级别Propagation定义了Transaction的边界,我们可以很方便的在@Transactional注解中定义不同的传播级别。

    下面我们来分别看一下Transaction的传播级别。

    REQUIRED

    REQUIRED是默认的传播级别,下面的两种写法是等价的:

        @Transactional    public void deleteBookWithDefaultTransaction(Long id) {        bookRepository.deleteBookById(id);    }    @Transactional(propagation = Propagation.REQUIRED)    public void deleteBookWithRequired(Long id) {    }

    Spring会检测现在是否有一个有效的transaction。如果没有则创建,如果有transaction,则Spring将会把该放方法的业务逻辑附加到已有的transaction中。

    我们再看下REQUIRED的伪代码:

    if (isExistingTransaction()) {    if (isValidateExistingTransaction()) {        validateExisitingAndThrowExceptionIfNotValid();    }    return existing;}return createNewTransaction();

    SUPPORTS

    在SUPPORTS的情况下,Spring首先会去检测是否有存在Transaction,如果存在则使用,否则不会使用transaction。

    我们看下代码怎么使用:

        @Transactional(propagation = Propagation.SUPPORTS)    public void deleteBookWithSupports(Long id) {    }

    SUPPORTS的实现伪代码如下:

    if (isExistingTransaction()) {    if (isValidateExistingTransaction()) {        validateExisitingAndThrowExceptionIfNotValid();    }    return existing;}return emptyTransaction;

    MANDATORY

    在MANDATORY情况下,Spring先会去检测是否有一个Transaction存在,如果存在则使用,否则抛出异常。

    我们看下代码怎么使用:

        @Transactional(propagation = Propagation.MANDATORY)    public void deleteBookWithMandatory(Long id) {    }

    MANDATORY的实现逻辑如下:

    if (isExistingTransaction()) {    if (isValidateExistingTransaction()) {        validateExisitingAndThrowExceptionIfNotValid();    }    return existing;}throw IllegalTransactionStateException;

    NEVER

    如果是NEVER的情况下,如果现在有一个Transaction存在,则Spring会抛出异常。

    使用的代码如下:

        @Transactional(propagation = Propagation.NEVER)    public void deleteBookWithNever(Long id) {    }

    实现逻辑代码如下:

    if (isExistingTransaction()) {    throw IllegalTransactionStateException;}return emptyTransaction;

    NOT_SUPPORTED

    如果使用的是NOT_SUPPORTED,那么Spring将会首先暂停现有的transaction,然后在非transaction情况下执行业务逻辑。

    我们这样使用:

        @Transactional(propagation = Propagation.NOT_SUPPORTED)    public void deleteBookWithNotSupported(Long id) {    }

    REQUIRES_NEW

    当REQUIRES_NEW使用时,Spring暂停当前的Transaction,并创建一个新的。

    我们看下代码怎么使用:

        @Transactional(propagation = Propagation.REQUIRES_NEW)    public void deleteBookWithRequiresNew(Long id){    }

    相应的实现代码如下:

    if (isExistingTransaction()) {    suspend(existing);    try {        return createNewTransaction();    } catch (exception) {        resumeAfterBeginException();        throw exception;    }}return createNewTransaction();

    NESTED

    NESTED顾名思义,是嵌套的Transaction,Spring首先检查transaction是否存在,如果存在则创建一个savepoint,如果我们的程序抛出异常的时候,transaction将会回滚到该savepoint。如果没有transaction,NESTED的表现和REQUIRED一样。

    我们看下怎么使用:

        @Transactional(propagation = Propagation.NESTED)    public void deleteBookWithNested(Long id){    }

    Transaction的隔离级别

    隔离级别就是我们之前提到的原子性,一致性,隔离性和持久性。隔离级别描述了改动对其他并发者的可见程度。

    隔离级别主要是为了防止下面3个并发过程中可能出现的问题:

    1. 脏读: 读取一个transaction还没有提交的change
    2. 不可重复读:在一个transaction修改数据库中的某行数据时,另外一个transaction多次读取同一行数据,获取到的不同的值。
    3. 幻读: 在一个transaction添加或者删除数据库的数据时,另外一个transaction做范围查询,获得了不同的数据行数。

    READ_UNCOMMITTED

    READ_UNCOMMITTED是隔离级别中最低的级别。这个级别下,并发的3个问题都可能出现。

    我们这样使用:

        @Transactional(isolation = Isolation.READ_UNCOMMITTED)    public void deleteBookWithReadUncommitted(Long id){    }

    READ_COMMITTED

    READ_COMMITTED可以防止脏读。

    我们看下代码:

        @Transactional(isolation = Isolation.READ_COMMITTED)    public void deleteBookWithReadCommitted(Long id){    }

    REPEATABLE_READ

    REPEATABLE_READ可以防止脏读和不可重复读。

    使用的代码如下:

        @Transactional(isolation = Isolation.REPEATABLE_READ)    public void deleteBookWithRepeatableRead(Long id){    }

    SERIALIZABLE

    SERIALIZABLE是最严格的基本,可以防止脏读,不可重复读和幻读。

    我们看下怎么使用:

        @Transactional(isolation = Isolation.SERIALIZABLE)    public void deleteBookWithSerializable(Long id){    }

    欢迎关注我的公众号:程序那些事,更多精彩等着您!

    更多内容请访问:flydean的博客 flydean.com

    292cebe740970c5923955479cb3bc1ab.png
    展开全文
  • Spring中Transactional注解的使用: @Transactional(propagation=Propagation.REQUIRED,rollbackFor = Exception.class) 已经有事务则直接加入,没有事务则新建事务;遇到异常则回滚 1)定义事物传播行为介绍:  @...

    Spring中Transactional注解的使用:

        @Transactional(propagation=Propagation.REQUIRED,rollbackFor = Exception.class) 已经有事务则直接加入,没有事务则新建事务;遇到异常则回滚

    1)定义事物传播行为介绍:

      @Transactional(propagation=Propagation.REQUIRED) :如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)                 @Transactional(propagation=Propagation.REQUIRES_NEW) :不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务

      @Transactional(propagation=Propagation.MANDATORY) :必须在一个已有的事务中执行,否则抛出异常

      @Transactional(propagation=Propagation.NEVER) :必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)

      @Transactional(propagation=Propagation.SUPPORTS) :如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.

    @Transactional(propagation=Propagation.NOT_SUPPORTED) :容器不为这个方法开启事务

    2)事物超时设置:

    @Transactional(timeout=30) //默认是30秒

    3)事务隔离级别:

      @Transactional(isolation = Isolation.READ_UNCOMMITTED):读取未提交数据(会出现脏读, 不可重复读) 基本不使用

      @Transactional(isolation = Isolation.READ_COMMITTED):读取已提交数据(会出现不可重复读和幻读)

      @Transactional(isolation = Isolation.REPEATABLE_READ):可重复读(会出现幻读)

      @Transactional(isolation = Isolation.SERIALIZABLE):串行化

     MYSQL: 默认为REPEATABLE_READ级别

      SQLSERVER: 默认为READ_COMMITTED

    4)定义事物遇到哪些异常会回滚(rollback)

    用 spring 事务管理器,由spring来负责数据库的打开,提交,回滚.默认遇到运行期例外(throw new RuntimeException("注释");)会回滚,即遇到不受检查(unchecked)的例外时回滚;而遇到需要捕获的例外(throw new Exception("注释");)不会回滚,即遇到受检查的例外(就是非运行时抛出的异常,编译器会检查到的异常叫受检查例外或说受检查异常)时,需我们指定方式来让事务回滚要想所有异常都回滚,要加上 @Transactional( rollbackFor={Exception.class,其它异常}) .

    5)使用注意点

    、@Transactional 注解应该只被应用到 public 可见度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错, 但是这个被注解的方法将不会展示已配置的事务设置。

     

    http://www.cnblogs.com/caoyc/p/5632963.html

     

     

    Ssm框架下使用数据库的事物:

     

    首先确认数据库的引擎类型,如果是myisam则不支持使用事物,修改为InnoDB支持事物.

    show engines;//查看数据库引擎 类型

    alter table task_baseinfo type=innodb;//修改task_baseinfo引擎类型为innodb

    1. spring的配置文件中 声明 和 开启 事物管理

            <!-- 开启事物管理 -->

        <tx:annotation-driven transaction-manager="transactionManager"/>

        <!-- (事务管理)transaction manager, use JtaTransactionManager for global tx -->

        <bean id="transactionManager"

            class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

            <property name="dataSource" ref="dynamicDataSource" />

        </bean>

    2)对公共方法增加事物管理

    //  @Transactional(readOnly=false,propagation=Propagation.REQUIRED,rollbackFor = Exception.class)

        @Transactional

        @Override

        public int saveTaskBaseInfo(int taskId, String taskName, int taskType,

               int funcNum, String lastModifiedUser,String allbefroeTaskId, String startDate,int startTime,int endTime)  {

               // 如果任务中包含有 taskId则执行修改

               //新插入执行的操作

               int newTaskId=getMaxTaskId()+1;

               taskId = newTaskId   ;

               HashMap<String, Object> map = new HashMap<String, Object>();

               map.put("table_name", "task_baseinfo");

               map.put("col_name", "task_id,task_name,task_type,create_time,last_modified_user,isdel");

               map.put("col_value", "("+newTaskId+",'"+taskName+"',"+taskType+",'"+DateUtil.getNowDateAsStr()+"','"+lastModifiedUser+"'"+",0"+")");

               mysqlAlterService.addRecord(map);//第一个插入操作

              

           int recode=taskTimeInfoService.insertTaskTimeInfo(taskId, startDate, startTime, endTime);//第二个插入操作,如果插入有问题,则返回的值为负数

                  if(recode<0){

                      throw new RuntimeException("该任务的定时信息插入失败...");//此处抛出的RuntimeException十分重要,只有抛出了该异常才会调用事物管理,此时抛出运行时异常说明第二个插入有问题.所以两个插入均失败;虽然第一个插入操作已经执行,但是此时会自动回滚.

                  }

           return 1000;

        }

    展开全文
  • 自己之前面试被问@Transactional注解哪些场景下会失效,一时语塞致使面试失败。所以今天简单和大家分享一下@Transactional...使用@Transactional注解时需要注意许多细节,不然你会发现@Transactional总是莫名其...
  • @Transactional 注解应该大家在开发过程中使用过了,但大家是否碰到过其失效的情况呢?我这边大概整理下失效产生的情况。...可以有基于TX和AOP的xml配置文件方式以及基于@Transactional注解的方...
  • Transactional注解即可以注在在Interface方法上。又可以写在具体Class上。而如果标注在Interface上且配置Spring AOP使用CGLib动态代理。会导致Transactional失效。原因看上面分析。 2.不能将Transactional标注在...
  • 1. 在需要事务管理地方加@Transactional 注解。@Transactional 注解可以被应用于接口定义和接口方法、类定义和类 ...2.@Transactional注解只能应用到 public 可见度方法上。 如果你在 protected、privat...
  • 5分钟搞懂Spring事务及@Transactional注解的使用切面常识Spring 事务代码反例分析: 切面常识 事务是一种切面业务。事务的开启和提交、回滚是在切面上完成的,也就是代理对象所新增的部分。 也就是需要用事务的时候...
  • spring事物配置,声明式事务管理和基于@Transactional注解的使用 ...
  • 一般情况下我们在处理具体的业务都是在Service层来进行处理操作,此时如果在Service类上添加@Transactional注解的话,那么Service曾的每一个业务方法调用的时候都会打开一个事务。 注意点: Spring默认情况下会对...
  • spring事务配置,声明式事务管理和基于@Transactional注解的使用
  • 在当前项目当中可以利用@Transactiona注解来管理事务,然后将不必要炒作进行回滚但是在操作过程当中要注意以下这些地方: 这里面有几点需要大家留意: A. 一个功能是否要事务,必须纳入设计、编码考虑。不能...
  • 事务管理对于企业应用来说是至关重要,即使出现异常情况,它也可以保证数据一致性。 Spring Framework对事务管理提供了一致抽象,其特点如下: 为不同事务API提供一致编程模型,比如JTA(Java Transaction...
  • 之后的文章将介绍:propagation(事务传播)和isolation(隔离性)等属性的使用事务使用的陷阱有哪些以及如何避免JPA和事务管理很重要的一点是JPA本身并不提供任何类型的声明式事务管理。如果在依赖注入容器之外使用JPA...
  • 在网上查找相关资料得知大多数时候用的是@Transactional这个注解,关于这个注解的使用方法和自己遇到的问题我整理为以下几点: 第一步:除了Spring MVC的ApplicationContext的常规配置(诸如dataSource之类),需要...
  • 1.@Transactional默认传播属性为Propagation.REQUIRED 2.同一个类中事务方法相互调用, 只会存在一个事务, 包括Propagation.REQUIRES_NEW 3.同一个类中, 普通方法调用事务方法, 该事务无效 4.Propagation.REQUIRES...
  • 先来看看异常分类error是一定会回滚这里Exception是异常,他又分为运行时异常RuntimeException和非运行时异常 可查异常(checked exceptions):Exception下除了RuntimeException外异常不可查异常...
  • https://www.cnblogs.com/jpfss/p/11152264.html
  • 前言 事务管理是应用系统开发中必不可少...声明式事务管理使业务代码逻辑不受污染, 因此在实际使用中声明式事务用比较多。声明式事务有两种方式,一种是在配置文件(xml)中做相关事务规则声明,另一种是基于...
  • 编程式事务:是指在代码中手动管理事务提交、回滚等操作,代码侵入性比较强,如下示例: try { //TODO something transactionManager.commit(status); } catch (Exception e) { transactionManager....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,515
精华内容 1,006
关键字:

transactional注解的使用