fdx 321 . github . io/2016/09/18/Spring % E4 % BA % 8B % E5 % 8A % A1 % E7 % AE % A1 % E7 % 90% 86//
1.关键类别
public interface PlatFormTransactionManager {
TransactionStatus getTransaction(
TransactionDefinition定义)引发TransactionException
voidcommit(TransactionStatus状态)引发TransactionException
voidrollback(TransactionStatus状态)引发TransactionException
}
事务的真正启动、提交和回滚是通过PlatformTransactionManager来实现的,比如我们常用的org . spring framework . JDBC . data source . data source transaction manager。
事务定义用于获取事务的一些属性,如隔离、传播、超时、只读,还定义了常数,如事务隔离级别和传播属性。
TransactionStatus用于设置和查询事务的状态,如是否是新事务、是否有保存点、RollbackOnly的设置和查询等。
2.声明性事务
声明式事务意味着通过配置节省大量代码,这样Spring就可以帮助您管理事务。本质上就是用round模式配置一个AOP,在执行方法之前用TransactionInterceptor截取,然后在事务开始之前调用一个PlatformTransactionManager的实现做一些事情,在方法执行之后再调用一个PlatformTransactionManager的实现做提交或者回滚。如图所示:
声明性事务可以通过XML、注释或两者的组合来配置。项目中通常有两种组合方式,即用XML配置数据源和事务管理器,然后通过@Transactional配置AOP(这个注解可以在类和方法上注明)。(个人认为用XML配置AOP相关配置比较繁琐,最好标注一下。)例如:
& lt!-数据源定义,使用dbcp数据源->:
& ltbean id = " DATaSOURce " class = " org . Apache . commons . dbcp . basicDATaSOURce " >的类。
& ltproperty name = " Driverclassname " value = " com . MySQL . JDBC . Driver " & gt;& lt/property>。
& ltproperty name = " URL " value = " JDBC:MySQL://localhost:3306/test " >& lt/property>。
& ltproperty name = " username " value = " root " >& lt/property>。
& ltproperty name = " password " value = " ali88 " & gt。& lt/property>。
& lt/bean>。
& lt!-事务管理器->:
& lttx:注释驱动的事务管理器= "txManager"/>。
& ltbean id = " TxManager " class = " org . spring framework . JDBC . data source . DataSourceTranctionManager " & gt;
& lt属性名称= "数据源" ref= "数据源"/>;
& lt/bean>。
& lt!- JDBC模板->:
& ltbean id = " JDBC template " class = " org . spring framework . JDBC . core . JDBC template " & gt;
& lt属性名= "数据源" >;
& ltref bean= "数据源"/>;
& lt/property>。
& lt/bean>。
@事务性(只读=真)
publicclassDefaultFooService{
public foo GetFoo(String FooName){
//做点什么
}
@Transactional(readOnly = false,传播=传播。需要_新)
publicvoidupdateFoo(Foo foo){
//做点什么
}
}
3.交易属性
引用正式文件的表格
value,在有多个事务管理器存在的情况下,用于标识使用哪个事务管理器isolation,事务的隔离级别,默认是Isolation.DEFAULT,这个DEFAULT是和具体使用的数据库相关的。关于隔离级别,可以参考MySQL事务学习总结readOnly, 是否只读,如果配置了true,但是方法里使用了update,insert语句,会报错。对于只读的事务,配置为true有助于提高性能。rollbackFor, noRollbackFor. Spring的声明式事务的默认行为是如果方法抛出RuntimeException或者Error,则事务会回滚,对于其他的checked类型的异常,不会回滚。如果想改变这种默认行为,可以通过这几个属性来配置。propagation, 后面会具体讲。4.事务沟通机制
类型说明PROPAGATION_REQUIRED如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是 最常见的选择。PROPAGATION_SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行PROPAGATION_MANDATOR使用当前的事务,如果当前没有事务,就抛出异常PROPAGATION_REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起PROPAGATION_NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务,就把当前事务挂起PROPAGATION_NEVER以非事务方式执行,如果当前存在事务,则抛出异常PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与 PROPAGATION_REQUIRED 类似的操作其他的很容易理解,下面用例子介绍三种传播级别:PROGRAMME _ REQUIRED、PROGRAMME _ REQUIRED _ new和PROGRAMME _ NESTED。
表格结构和原始数据
mysql>。从测试中选择*
+ - + - +
| id |货币|
+ - + - +
| 3| 500|
| 5| 500|
| 7| 600|
+ - + - +
成套3次(0.00秒)
PROPAGATION_REQUIRED@服务
publicclassMysqlTest01 {
@自动连线
privateJDBC template JDBC template;
@自动连线
privateMySQL test 02 MySQL test 02;
@事务性
publicvoidtest(){
JDBC Template . execute(" update test set money = ' 501 '其中id = 3 ");
尝试{
MySQL test 02 . test();
}捕获(例外e) {
System.out.println("第二次交易异常");
}
}
}
@服务
classMysqlTest02 {
@自动连线
privateJDBC template JDBC template;
@事务性(传播=传播。必需)
publicvoidtest(){
JDBC Template . execute(" update test set money = ' 502 '其中id = 3 ");
throwNewRuntimeException();
}
}
执行后,测试表的数据没有变化。
因为MysqlTest02中的事务传播类型是传播。必选,逻辑上有两个事务,但是底层共享一个物理事务,第二个事务抛出RuntimeExcetion,导致事务回滚。对于这种传播类型,内层事务的回滚将导致外层事务回滚。所以数据库中的数据没有变化。
PROPAGATION_REQUIRES_NEW@服务
publicclassMysqlTest01 {
@自动连线
privateJDBC template JDBC template;
@自动连线
privateMySQL test 02 MySQL test 02;
@事务性
publicvoidtest(){
JDBC Template . execute(" update test set money = ' 501 '其中id = 3 ");
尝试{
MySQL test 02 . test();
}捕获(例外e) {
System.out.println("第二次交易异常");
}
}
}
@服务
classMysqlTest02 {
@自动连线
privateJDBC template JDBC template;
@事务性(传播=传播。需要_新)
publicvoidtest(){
JDBC Template . execute(" update test set money = ' 502 '其中id = 3 ");
throwNewRuntimeException();
}
}
同样的代码,唯一不同的是第二个事务的传播属性改为REQUIRES_NEW,执行结果如何?抱歉,第二笔交易无法执行。
对于REQUIRES_NEW,逻辑上有两个事务,底部有两个物理事务。由于第一个事务和第二个事务更新相同的记录,第一个事务将为Mysql的默认隔离级别REPEATABLE-READ向记录添加排他锁,因此第二个事务被卡住了。
好了,让我们把执行第二个事务的SQL语句替换为。
更新test setmoney = '501 ',其中id = 5 "
执行结果如下,可以看到只有第二个事务回滚。
mysql>。从测试中选择*
+ - + - +
| id |货币|
+ - + - +
| 5| 500|
| 3| 501|
| 7| 600|
+ - + - +
成套3次(0.00秒)
PROPAGATION_NESTED对于这种传播类型,物理上只有一个事务,但可以有多个保存点来回滚动。当然,使用这种类型的传播需要数据库支持保存点,并且jdbc必须高于3.0版本(这并不确定)。
@服务
publicclassMysqlTest01 {
@自动连线
privateJDBC template JDBC template;
@自动连线
privateMySQL test 02 MySQL test 02;
@自动连线
privateMySQL test 03 MySQL test 03;
@事务性
publicvoidtest(){
JDBC Template . execute(" update test set money = ' 501 '其中id = 3 ");
尝试{
MySQL test 02 . test();
}捕获(例外e) {
System.out.println("第二次交易异常");
}
MySQL test 03 . test();
}
}
@服务
classMysqlTest02 {
@自动连线
privateJDBC template JDBC template;
@事务性(传播=传播。嵌套)
publicvoidtest(){
JDBC Template . execute(" update test set money = ' 502 '其中id = 3 ");
throwNewRuntimeException();
}
}
@服务
classMysqlTest03 {
@自动连线
privateJDBC template JDBC template;
@事务性(传播=传播。嵌套)
publicvoidtest(){
JDBC Template . execute(" update test set money = ' 503 '其中id = 3 ");
}
}
执行结果如下。可以看到,第一个事务和第三个事务提交成功,第二个事务回滚。物理上,它们在同一个事务中,只使用保存点技术。
mysql>。从测试中选择*
+ - + - +
| id |货币|
+ - + - +
| 5| 500|
| 3| 501|
| 7| 601|
+ - + - +
3套(0.01秒)
5.其他人
写测试代码的时候,遇到了一个关于AOP的问题。你可以看到我的测试代码,每个事务都写在一个新的类中。为什么不这样写呢?
@服务
publicclassMysqlTest01 {
@自动连线
privateJDBC template JDBC template;
@事务性
publicvoidtest01(){
JDBC Template . execute(" update test set money = ' 501 '其中id = 3 ");
test 02();
}
@事务性
publicvoidtest02(){
JDBC Template . execute(" update test set money = ' 501 '其中id = 5 ");
}
}
这是因为test01调用Spring的test02、test02 AOP,test02不会被AOP截获,所以不会被Spring管理。原因是Spring AOP的实现本质是通过动态代理执行真正的方法,然后在代理类中做一些额外的事情。通过其他类调用MysqlTest01中的test01方法时,其实是因为使用Spring的DI而注入的MysqlTest01的代理类,但通过内部方法调用test02时就不是了。
6.引文
Spring框架参考文档
1.《spring事务传播机制 Spring 事务管理》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。
2.《spring事务传播机制 Spring 事务管理》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。
3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/junshi/1178454.html