事务的隔离级别和事务传播参考:https://blog.csdn.net/sumengnan/article/details/112620349
一、编程式事务
需要手动编程来实现事务,粒度可以控制到代码块级别
(1)原始事务管理方式
关键的三个接口:TransactionDefinition、PlatformTransactionManager、TransactionStatus
1、TransactionDefinition(事务定义接口)
默认实现类是DefaultTransactionDefinition。用于定义一个事务的静态属性。比如事务的传播行为、超时时间等。可以自定义类实现Transactiondefinition接口。
2、PlatformTransactionManager(事务管理器接口)
实现类如下:
根据不同的ORM框架,主要分成如下几类:
- DateSourceTransactionManager:适用于JDBC和iBatis持久化操作。
- HibernateTransactionManager:适用于Hibernate框架
- JpaTransactionManager:适用于使用JPA持久化操作
- JtaTransactionManager:适用于分布式事务操作
- 其他
3、TransactionStatus:(事务状态接口)
TransactionStatus继承了SavepointManager接口,SavepointManager是对事务中上述保存点功能的封装。
用法:通过PlatformTransactionManager.getTransaction(...)方法返回TransactionStatus对象。提供了一个简单的控制事务执行和查询事务状态的方法。例如是否是一个新的事务、是否已被标记为回滚
代码示例:
@Autowired
private PlatformTransactionManager dataSourceTransactionManager;
//-------------------------------------------------------分割,上面是注入,下面是关键代码
TransactionStatus transactionStatus=null;
try {
transactionStatus = dataSourceTransactionManager.getTransaction(new DefaultTransactionDefinition());//手动开启事务
//此处进行业务操作
dataSourceTransactionManager.commit(transactionStatus);//提交
} catch (Exception e) {
if(StringMoreUtils.checkValNotNull(status)){
dataSourceTransactionManager.rollback(transactionStatus);//回滚
}
e.printStackTrace();
}
(2)TransactionTemplate模版模式方式(推荐)
实际上是对上面原始事务管理方式的一个封装,继承关系如下:
TransactionTemplate
主要依赖TransactionTemplate.execute( ... ),
执行事务管理的时候,传入的参数有两种选择:
1、TransactionCallback(
有返回值)
2、TransactionCallbackWithoutResult(
无返回值)
代码示例:
//有返回值
transactionTemplate.execute(new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus transactionStatus) {
try {
//....... 业务代码
return new Object();//有返回值
} catch (Exception e) {
//回滚
transactionStatus.setRollbackOnly();
return null;
}
}
});
//无返回值
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
try {
// .... 业务代码
} catch (Exception e){
//回滚
transactionStatus.setRollbackOnly();
}
}
});
二、声明式事务
通过aop方式来实现,粒度只能控制到方法级别
(1)通过xml方式配置,基于tx和aop名字空间
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置 transactionTemplate -->
<bean id="transactionTemplate"
class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
</bean>
< tx:advice id ="txAdvice" transaction-manager ="transactionManager" >
< tx:attributes >
< tx:method name ="*" propagation ="REQUIRED" />
</ tx:attributes >
</ tx:advice >
< aop:config >
< aop:pointcut id ="interceptorPointCuts"
expression ="execution(* com.bluesky.spring.dao.*.*(..))" />
< aop:advisor advice-ref ="txAdvice"
pointcut-ref ="interceptorPointCuts" />
</ aop:config >
(2)通过@Transaction注解的方式来声明事务(推荐)
@Transactional
public List<SysUser> findByLogin(String username, String password) {
return this.sysUserRepository.findByLogin(username, password);
}
三、@Transaction注解的原理:
例如:我调用service的findByLogin这个方法
1、在项目启动时,spring会创建JpaTransactionManager实例( springboot-autoconfiguration包下)
还有事务拦截器(动态代理层面的,可不是springmvc的那个)
2、同时初始化spring ioc容器时会为我的service类创建动态代理类,通过下面的类
3、当service类的方法被调用时,会先调用代理类JdkDynamicAopProxy的invoke方法
反射调用ReflectiveMethodInvocation的proceed方法
再调用事务拦截器的invoke方法
再调用TransactionInterceptor的抽象父类的TransactionAspectSupport的invokeWithinTransaction方法
还是invokeWithinTransaction方法,里面看到了熟悉的编程式事务中的execute方法,还有JapTransactionManager、TransactionCallback等类。
说明声名式事务底层也是使用的编程式事务
截图中:invocation.proceedWithInvocation()方法,其实就是调用service的findByLogin这个方法
至此事务流程完毕
四、@Transaction注解失效的场景:
提示:spring建议不要在接口或接口方法山使用@Transactional注解,因为只有基于接口的代理才会生效。
(1)不支持事务场景:
- 数据库MyIsam引擎不支持事务。mysql5.5.5之后默认是InnoDB引擎,才支持事务。
- @Transactional(propagation = Propagation.NOT_SUPPORTED)以非事务方式运行。当然就不会有事务。
(2)配置层面不注意:
- 启动类没加@EnableTransactionManagement注解。(springboot-autoconfigure的DataSourceTransactionManagerAutoConfiguration类中添加了注解)
- 事务类没有加@Service注解。也就是没有被spring管理(没有被动态代理)。
- 数据源没有配置事务管理器。(PlatformTransactionManager接口的实现类,如JpaTransactionManager,datasourceTransactionManager)
(3)编码层面不注意:
- 只能在public方法上使用注解。如果要用在非 public 方法上,可以开启
AspectJ
代理模式 - 异常没有上抛。ry-catch捕获住了,应该上抛给代理类。上层代理类捕获到异常后进行回滚。
- @Transactional(rollbackFor = Exception.class)异常类型错误。注意:默认回滚RuntimeException异常
- 自身调用问题。只有从外部调用service事务方法,才会走动态代理,才可以使用事务。调用本类的其他方法不会走动态代理
四、@Transaction注解读写事务和只读事务:
默认是读写事务,加上@Transactional(readOnly = true)后变成只读事务。
(1)只读事务有什么作用?
- orm框架提升性能。例如:hibernate省去了检测和同步对象持久化的更新(不执行flush操作)。
- 不允许DML语句,否则报错。
- 适合报表或统计。可以在同一事务中查询多次查询,当其他事务同时修改数据,也不会影响到查询结果
(2)hibernate框架是如何对只读事务做优化的?
HibernateJpaDialect类是关键,它的beginTransaction方法是开始事务操作
(3)mysql如何开启只读事务?
set transaction read only;
开启只读事务此时间点,之后所有的查询都是此时间点的数据(其他事务所有的提交都不会改变查询结果),直到commit或rollBack或执行ddl语句。
set transaction read only;只能是只读事务的第一条语句,并且只能出现一次。之后的语句只能出现select、select into、open、fetch、close、lock table、commit、rollback等语句,
其他的都不能出现例如:update、或select * from table for update(添加排他锁)等。