Spring声明式事务让我们从复杂的事务处理中得到解脱。使得我们再也无需要去处理获得连接、关闭连接、事务提交和回滚等这些操作。再也无需要我们在与事务相关的方法中处理大量的try…catch…finally代码。我们在使用Spring声明式事务时,有一个非常重要的概念就是事务属性。事务属性通常由事务的传播行为,事务的隔离级别,事务的超时值和事务只读标志组成。我们在进行事务划分时,需要进行事务定义,也就是配置事务的属性。Spring在TransactionDefinition接口中定义这些属性,以供PlatfromTransactionManager使用,PlatfromTransactionManager是spring事务管理的核心接口。Java代码1.TransactionDefinition2.publicinterfaceTransactionDefinition{3.intgetPropagationBehavior();4.intgetIsolationLevel();5.intgetTimeout();6.booleanisReadOnly();7.}getTimeout()方法,它返回事务必须在多少秒内完成。isReadOnly(),事务是否只读,事务管理器能够根据这个返回值进行优化,确保事务是只读的。getIsolationLevel()方法返回事务的隔离级别,事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据。在TransactionDefinition接口中定义了五个不同的事务隔离级别ISOLATION_DEFAULT这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.另外四个与JDBC的隔离级别相对应ISOLATION_READ_UNCOMMITTED这是事务最低的隔离级别,它充许别外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。例如:Mary的原工资为1000,财务人员将Mary的工资改为了8000,但未提交事务Java代码1.Connectioncon1=getConnection();2.con.setAutoCommit(false);3.updateemployeesetsalary=8000whereempId=Mary;与此同时,Mary正在读取自己的工资Java代码1.Connectioncon2=getConnection();2.selectsalaryfromemployeewhereempId=Mary;3.con2.commit();Mary发现自己的工资变为了8000,欢天喜地!而财务发现操作有误,而回滚了事务,Mary的工资又变为了1000Java代码1.//con12.con1.rollback();像这样,Mary记取的工资数8000是一个脏数据。ISOLATION_READ_COMMITTED保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。ISOLATION_REPEATABLE_READ这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。在事务1中,Mary读取了自己的工资为1000,操作并没有完成Java代码1.con1=getConnection();2.selectsalaryfromemployeeempId=Mary;在事务2中,这时财务人员修改了Mary的工资为2000,并提交了事务.Java代码1.con2=getConnection();2.updateemployeesetsalary=2000;3.con2.commit();在事务1中,Mary再次读取自己的工资时,工资变为了2000Java代码1.//con12.selectsalaryfromemployeeempId=Mary;在一个事务中前后两次读取的结果并不致,导致了不可重复读。使用ISOLATION_REPEATABLE_READ可以避免这种情况发生。ISOLATION_SERIALIZABLE这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。目前工资为1000的员工有10人。事务1,读取所有工资为1000的员工。Java代码1.con1=getConnection();2.Select*fromemployeewheresalary=1000;共读取10条记录这时另一个事务向employee表插入了一条员工记录,工资也为1000Java代码1.con2=getConnection();2.Insertintoemployee(empId,salary)values(Lili,1000);3.con2.commit();事务1再次读取所有工资为1000的员工Java代码1.//con12.select*fromemployeewheresalary=1000;共读取到了11条记录,这就产生了幻像读。ISOLATION_SERIALIZABLE能避免这样的情况发生。但是这样也耗费了最大的资源。getPropagationBehavior()返回事务的传播行为,由是否有一个活动的事务来决定一个事务调用。在TransactionDefinition接口中定义了七个事务传播行为。PROPAGATION_REQUIRED如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。Java代码1.//事务属性PROPAGATION_REQUIRED2.methodA{3.……4.methodB();5.……6.}7.8.//事务属性PROPAGATION_REQUIRED9.methodB{10.……11.}使用spring声明式事务,spring使用AOP来支持声明式事务,会根据事务属性,自动在方法调用之前决定是否开启一个事务,并在方法执行之后决定事务提交或回滚事务。单独调用methodB方法Java代码1.main{2.metodB();3.}相当于Java代码1.Main{2.Connectioncon=null;3.4.rry{5.con=getConnection();6.con.setAutoCommit(false);7.//方法调用8.methodB();9.//提交事务10.con.commit();11.}12.Catch(RuntimeExceptionex){13.//回滚事务14.con.rollback();15.}16.finally{17.//释放资源18.closeCon();19.}20.}Spring保证在methodB方法中所有的调用都获得到一个相同的连接。在调用methodB时,没有一个存在的事务,所以获得一个新的连接,开启了一个新的事务。单独调用MethodA时,在MethodA内又会调用MethodB.执行效果相当于Java代码1.main{2.Connectioncon=null;3.try{4.con=getConnection();5.methodA();6.con.commit();7.}8.cathc(RuntimeExceptionex){9.con.rollback();10.}11.finally{12.closeCon();13.}14.}调用MethodA时,环境中没有事务,所以开启一个新的事务.当在MethodA中调用MethodB时,环境中已经有了一个事务,所以methodB就加入当前事务。PROPAGATION_SUPPORTS如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。但是对于事务同步的事务管理器,PROPAGATION_SUPPORTS与不使用事务有少许不同。Java代码1.//事务属性PROPAGATION_REQUIRED2.methodA(){3.methodB();4.}5.6.//事务属性PROPAGATION_SUPPORTS7.methodB(){8.……9.}单纯的调用methodB时,methodB方法是非事务的执行的。当调用methdA时,methodB则加入了methodA的事务中,事务地执行。PROPAGATION_MANDATORY如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。Java代码1.//事务属性PROPAGATION_REQUIRED2.methodA(){3.methodB();4.}5.6.//事务属性PROPAGATION_MANDATORY7.methodB(){8.……9.}当单独调用methodB时,因为当前没有一个活动的事务,则会抛出异常thrownewIllegalTransactionStateException(Transactionpropagation'mandatory'butnoexistingtransactionfound);当调用methodA时,methodB则加入到methodA的事务中,事务地执行。PROPAGATION_REQUIRES_NEW总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。Java代码1.//事务属性PROPAGATION_REQUIRED2.methodA(){3.doSomeThingA();4.methodB();5.doSomeThingB();6.}7.8.//事务属性PROPAGATION_REQUIRES_NEW9.methodB(){10.……11.}当单独调用methodB时,相当于把methodb声明为REQUIRED。开启一个新的事务,事务地执行。当调用methodA时Java代码1.main(){2.methodA();3.}情况有些大不一样.相当于下面的效果。Java代码1.main(){2.TransactionManagertm=null;3.try{4.//获得一个JTA事务管理器5.tm=getTransactionManager();6.tm.begin();//开启一个新的事务7.Transactionts1=tm.getTransaction();8.doSomeThing();9.tm.suspend();//挂起当前事务10.try{11.tm.begin();//重新开启第二个事务12.Transactionts2=tm.getTransaction();13.methodB();14.ts2.commit();//提交第二个事务15.16.}17.Catch(RunTimeExceptionex){18.ts2.rollback();//回滚第二个事务19.}20.finally{21.//释放资源22.}23.//methodB执行完后,复恢第一个事务24.tm.resume(ts1);25.doSomeThingB();26.ts1.commit();//提交第一个事务27.}28.catch(RunTimeExceptionex){29.ts1.rollback();//回滚第一个事务30.}31.finally{32.//释放资源33.}34.}在这里,我把ts1称为外层事务,ts2称为内层事务。从上面的代码可以看出,ts2与ts1是两个独立的事务,互不相干。Ts2是否成功并不依赖于ts1。如果methodA方法在调用methodB方法后的doSomeThingB方法失败了,而methodB方法所做的结果依然被提交。而除了methodB之外的其它代码导致的结果却被回滚了。使用PROPAGATION_REQUIRES_NEW,需要使用JtaTransactionManager作为事务管理器。PROPAGATION_NOT_S