Transaction
- JDBC事务
- Spring 声明式事务
- Spring 编程式事务
- Spring 声明式事务-事务失效
JDBC Connection事务
setAutoCommit(false)
1
2
3
4
5
6
7
8
9
10// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
// so we don't want to do it unnecessarily (for example if we've explicitly
// configured the connection pool to set it already).
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
}execute SQL…
Commit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
protected void doCommit(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Committing JDBC transaction on Connection [" + con + "]");
}
try {
con.commit();
}
catch (SQLException ex) {
throw translateException("JDBC commit", ex);
}
}
protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
}
try {
con.rollback();
}
catch (SQLException ex) {
throw translateException("JDBC rollback", ex);
}
}
Spring 声明式事务
其实就是将事务代码和业务代码隔离开发, 然后通过一段配置让他们组装运行, 最后达到事务控制的目的.
声明式事务就是通过AOP原理实现的.
优势
- 非侵入式开发,Spring推荐的开发方式,保证业务代码不受污染,只要加上@Transactional(rollbackFor = Exception.class)即可
不足
- 最细粒度只能作用到方法级别。但可以通过抽取事务代码为独立代码块进行处理
原理
@EnableTransactionManagement
利用TransactionManagementConfigurationSelector给容器中导入两个组件:
- AutoProxyRegistrar
- ProxyTransactionManagementConfiguration
AutoProxyRegistrar
- 给容器中注册一个InfrastructureAdvisorAutoProxyCreator后置处理器组件
- InfrastructureAdvisorAutoProxyCreator 利用后置处理器机制在对象创建后,包装一个代理类对象返回,代理对象执行方法利用拦截器进行调用
ProxyTransactionManagementConfiguration
- 给容器中注册事务增强器事务增强器要用事务的注解信息
- AnnotationTransactionAttributeSource解析事务注解
事务拦截器
- TransactionInterceptor,保存了事务属性信息,事务管理器
这是一个MethodInterceptor;
在目标方法执行的时候:执行拦截器链(事务拦截器)- 先获取事务相关的属性
- 再获取PlatformTransactionManager,如果事先没有添加指定任何TransactionManager,最终会从容器中按照类型获取一个PlatformTransactionManager;
- 执行目标方法
如果异常,获取到事务管理器,利用事务管理回滚操作,
如果正常,利用事务管理器,提交事务
Spring 编程式事务
将业务代码和事务代码放在一起书写,耦合性太高,不推荐使用
Transaction Template
模版方法,经常Template + Callback配合使用
事务获取
//org.springframework.transaction.support.TransactionTemplate#execute try { result = action.doInTransaction(status); }
1
2
3
4
5
6
7
8
- connection此时是打开的,此时尽量不要做耗时操作,如调用外域(Dubbo)、读写本地文件 等
- 多次注册事务形为
```java
methodA() | insert |
methodB() | insert |
事务同步器 TransactionSynchronizationManager
- 事务结束,后置处理,可以做成 同步/异步 方法调用,建议做成异步(如ThreadLocal)调用
- 持久化
- 后置处理如果是 同步状态,使用TransactionSynchronizationManager再开事务不会再执行,不建议事务结束后再开事务
Spring 声明式事务失效
访问权限问题
例如:private修饰的方法,Spring要求被代理的方法必须是public的
方法用final修饰
方法内部调用
同一个类中a方法调用有事务注解的b方法
Spring在扫描Bean的时候会自动为标注了@Transactional注解的类生成一个代理类(proxy),当有注解的方法被调用的时候,实际上是代理类调用的,代理类在调用之前会开启事务,执行事务的操作,但是同类中的方法互相调用,相当于this.B(),此时的B方法并非是代理类调用,而是直接通过原有的Bean直接调用(相当于new Bean.b(),此时不受代理类控制),所以注解会失效。
解决方法
- 1)将B方法移到另一个service中
- 2)在service中注入自己
- 3)通过aopContent类:在该service类中调用AopContent.currentProxy ()获取代理对象
解决原理
相当于b方法变为代理对象的方法,相当于代理对象调用,不会因new Bean导致事务失效