默认 @Transactional 注解式事务

(1)@EnableTransactionManagement

正常情况下,我们是需要在 ApplicationConfig 类加上 @EnableTransactionManagement 注解才能开启事务管理。通过 DataSource 的研究步骤 spring.factories 里面默认加载 TransactionAutoConfiguration 类,而我们看源码,其里面已经加了此注解,默认采用的 AdviceMode.PROXY,所以默认情况的事务管理机制是代理方式的,通过添加 @Transactional 注解式配置方法,查看 SimpleJpaRepository:

@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepository<T, ID>, JpaSpecificationExecutor<T> {...}

所以每个 Respository 的方法是都是有默认的只读事务的。

(2)我们来查看一下 @Transactional 源码:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {@AliasFor("transactionManager")String value() default "";@AliasFor("value")String transactionManager() default "";Propagation propagation() default Propagation.REQUIRED;Isolation isolation() default Isolation.DEFAULT;int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;boolean readOnly() default false;Class<? extends Throwable>[] rollbackFor() default {};String[] rollbackForClassName() default {};Class<? extends Throwable>[] noRollbackFor() default {};String[] noRollbackForClassName() default {};
}

@Transactional 注解中常用参数说明

参数名称

功能描述

readOnly

rollbackFor

rollbackForClassName

noRollbackFor

noRollbackForClassName

propagation

isolation

timeout

transactionManager/value

(3)propagation:传播行为

所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。

可以看 org.springframework.transaction.annotation.Propagation 枚举类中定义了 7 个表示传播行为的枚举值:

public enum Propagation { REQUIRED(0), SUPPORTS(1), MANDATORY(2), REQUIRES_NEW(3), NOT_SUPPORTED(4), NEVER(5), NESTED(6); }

  • REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
  • SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  • MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
  • REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
  • NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  • NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
  • NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于REQUIRED。

设置方法:通过使用propagation属性设置,例如:

@Transactional(propagation = Propagation.REQUIRED)

(4)Isolation 隔离级别

隔离级别是指若干个并发的事务之间的隔离程度,与我们开发时候主要相关的场景包括:脏读取、重复读、幻读。

我们可以看 org.springframework.transaction.annotation.Isolation 枚举类中定义了四个表示隔离级别的值:

public enum Propagation {REQUIRED(0),SUPPORTS(1),MANDATORY(2),REQUIRES_NEW(3),NOT_SUPPORTED(4),NEVER(5),NESTED(6);
}
  • DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别,对大部分数据库而言,通常这值就是READ_COMMITTED。
  • READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读和不可重复读,因此很少使用该隔离级别。
  • READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
  • REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读。
  • SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读,但是这将严重影响程序的性能。通常情况下也不会用到该级别。

设定方法为通过使用 isolation 属性设置,例如:

@Transactional(isolation = Isolation.DEFAULT)

(5)所以 Spring Boot 的这种默认机制,只需要在我们用事务时,在方法上或者此方法的类上加上 @Transactional注解即可。

而实际工作中,我们一般都要在 Service 层的某些方法上加事务,以保证整个方法的事务。示例如下:

@Transactional(rollbackOn = Exception.class)
public void saveUserInfo() throws Exception {userCustomerRepository.save(new UserCustomerEntity("jackzhang@mail.com","jackzhang"));userRepository.save(new UserInfoEntity("jack_test","name"));throw new Exception("test");......//此方法体有多个repository的调用,模拟异常,事务会回滚的
}

(6)注意的几点

  • @Transactional 只能被应用到 public 方法上,对于其他非 public 的方法,如果标记了 @Transactional 也不会报错,但方法没有事务功能。
  • 用 spring 事务管理器,由 spring 来负责数据库的打开、提交、回滚,默认遇到运行期例外(throw new RuntimeException("注释");)会回滚,即遇到不受检查(unchecked)的例外时回滚;而遇到需要捕获的例外(throw new Exception("注释");)不会回滚,即遇到受检查的例外(就是非运行时抛出的异常,编译器会检查到的异常叫受检查例外或说受检查异常)时,需我们指定方式来让事务回滚要想所有异常都回滚,要加上 @Transactional( rollbackFor={Exception.class,其他异常}),如果让 unchecked 例外不回滚:@Transactional(notRollbackFor=RunTimeException.class)。
  • @Transactional 注解应该只被应用到 public 可见度的方法上。如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错,但是这个被注解的方法将不会展示已配置的事务设置。
  • @Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上。然而,请注意仅仅 @Transactional 注解的出现不足于开启事务行为,它仅仅是一种元数据,能够被可以识别 @Transactional 注解和上述的配置适当的具有事务行为的 beans 所使用。上面的例子中,其实正是 元素的出现开启了事务行为。
  • Spring 团队的建议是你在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。当然可以在接口上使用 @Transactional 注解,但是这将只能当你设置了基于接口的代理时它才生效。因为注解是不能继承的,这就意味着如果你正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装(将被确认为严重的)。因此,请接受 Spring 团队的建议并且在具体的类上使用 @Transactional 注解。
  • 事务有两种配置方法,一种是我们现在说的显式的注解式事务,当我们注解式事务下,不加注解 service 方法上是没有任何事务的。还有一种是隐式事务,ASPECTJ 的思路配置方法,所以不是没有加 @Transactional 注解就一定没有事务。

声明式事务,又叫隐式事务,或者叫 ASPECTJ 事务

配置方法:

在实际工作中,每个方法都让我们加上 @Transactional 注解,可能工作量有点大,也有时候会忘,所以经常看到有开发团队配置拦截式事务。虽然 spring 官方不太推荐。只需要在我们的项目中新增一个类 AspectjTransactionConfig 即可,如下:

@Configuration
@EnableTransactionManagement
public class AspectjTransactionConfig {public static final String transactionExecution = "execution (* com.jackzhang.example..service.*.*(..))";@Autowiredprivate PlatformTransactionManager transactionManager;@Beanpublic DefaultPointcutAdvisor defaultPointcutAdvisor() {//指定一般要拦截哪些类AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();pointcut.setExpression(transactionExecution);//配置advisorDefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();advisor.setPointcut(pointcut);//指定不同的方法用不通的策略Properties attributes = new Properties();attributes.setProperty("get*", "PROPAGATION_REQUIRED,-Exception");attributes.setProperty("add*", "PROPAGATION_REQUIRED,-Exception");attributes.setProperty("save*", "PROPAGATION_REQUIRED,-Exception");attributes.setProperty("update*", "PROPAGATION_REQUIRED,-Exception");attributes.setProperty("delete*", "PROPAGATION_REQUIRED,-Exception");//创建InterceptorTransactionInterceptor txAdvice = new TransactionInterceptor(transactionManager, attributes);advisor.setAdvice(txAdvice);return advisor;}
}

这样我们的 Service 就会自动拥有了事务,可以加 @Transactional 来覆盖全局的配置。

Spring Data JPA 从入门到精通~事务的处理及其讲解相关推荐

  1. Spring Data JPA 从入门到精通~@Version处理乐观锁的问题

    @Version 处理乐观锁的问题 @Version 乐观锁介绍 我们在研究 Auditing 的时候,发现了一个有趣的注解 @Version,源码如下: package org.springfram ...

  2. Spring Data JPA 从入门到精通~自定义实现Repository

    EntityManager 的获取方式 我们既然要自定义,首先讲一下 EntityManager 的两种获取方式. 1. 通过 @PersistenceContext 注解. 通过将 @Persist ...

  3. spring data jpa从入门到精通_Spring Data JPA的简单入门

    前言 spring data JPA是spring团队打造的sping生态全家桶的一部分,本身内核使用的是hibernate核心源码,用来作为了解java持久层框架基本构成的样本是再好不过的选择.最近 ...

  4. Spring Data JPA 从入门到精通~Naming命名策略详解及其实践

    Naming 命名策略详解及其实践 用 JPA 离不开 @Entity 实体,我都知道实体里面有字段映射,而字段映射的方法有两种: 显式命名:在映射配置时,设置的数据库表名.列名等,就是进行显式命名, ...

  5. Spring Data JPA 从入门到精通~默认数据源的讲解

    默认数据源 spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://1 ...

  6. Spring Data JPA 从入门到精通~Auditing及其事件详解

    Auditing 及其事件详解 Auditing 翻译过来是审计和审核,Spring 的优秀之处在于帮我们想到了很多繁琐事情的解决方案,我们在实际的业务系统中,针对一张表的操作大部分是需要记录谁什么时 ...

  7. Spring Data JPA 从入门到精通~实际工作的应用场景

    在实际工作中,有哪些场景会用到自定义 Repository 呢,这里列出几种实际在工作中的应用案例. 1. 逻辑删除场景 可以用到上面说的两种实现方式,如果有框架级别的全局自定义 Respositor ...

  8. Spring Data JPA 从入门到精通~EntityManager介绍

    EntityManager 介绍 我们前面已经无数次提到了,JPA 的默认 Repository 的实现类是 SimpleJpaRepository,而里面的具体实现就是调用的 EntityManag ...

  9. Spring Data JPA 从入门到精通~JpaSpecificationExecutor示例

    新建两个实体 @Entity(name = "UserInfoEntity") @Table(name = "user_info", schema = &quo ...

最新文章

  1. python编程自学难吗-为什么很多人不建议自学python编程呢?
  2. 来入门一下kotlin吧
  3. uefi引导linux_使用UEFI双重引导Windows和Linux
  4. python 中的 __name__
  5. ORA-01092解决一例
  6. SQL Server 数据库所有表增加同一列
  7. 分表分页/跨库分页为什么这么难?
  8. python关闭csv文件_Python文件处理(txt、csv文件读取)
  9. 东北林业大学计算机科学与技术考研,东北林业大学计算机科学与技术2019年考研加试科目计算机网络专业课大纲...
  10. 想转行it , 培训还是自学?
  11. GitLab基础:如何将备份恢复至更高版本的GitLab
  12. Global Illumination_Screen-Space Ray Tracing(SSR)
  13. 文化中国 系列一:明朝的那些人儿
  14. 浴火重生的Firebird
  15. 推荐3款实用的PC端软件,工作生活两不误,每天5分钟悄悄成长
  16. C# 文件直接打印功能
  17. 面试必备:文本框与按钮的最简组合
  18. 筛法(线性筛,厄拉多塞筛)
  19. linux(shell)查看文件时间戳
  20. F5 iAPP 配置自动备份

热门文章

  1. 评测通知 | 2022年全国知识图谱与语义计算大会评测任务发布
  2. IJCAI 2018:中科院计算所:增强对话生成一致性的序列到序列模型
  3. “猜心思”的Hard模式:问答系统在智能法律场景的实践与优化
  4. 2021年北京交通大学925数据结构考研真题回忆版
  5. 4 计算机组成原理第三章 存储系统 高速缓冲存储器 虚拟存储器
  6. QNX下挂载USB设备
  7. Python学习---面向对象的学习[深入]
  8. mysql双机热备实现
  9. vb的VSFlexGrid控件
  10. Throwable 结构图