前段时间对Spring的事务配置作了比较深刻的研究,在此之间对Spring的事务配置虽然说也配置过,可是一直没有一个清楚的认识。经过此次的学习发觉Spring的事务配置只要把思路理清,仍是比较好掌握的。html
总结以下:java
Spring配置文件中关于事务配置老是由三个组成部分,分别是DataSource、TransactionManager和代理机制这三部分,不管哪一种配置方式,通常变化的只是代理机制这部分。程序员
DataSource、TransactionManager这两部分只是会根据数据访问方式有所变化,好比使用Hibernate进行数据访问时,DataSource实际为SessionFactory,TransactionManager的实现为HibernateTransactionManager。spring
具体以下图:sql
根据代理机制的不一样,总结了五种Spring事务的配置方式,配置文件以下:数据库
第一种方式:每一个Bean都有一个代理express
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml" /> <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> </bean> <!-- 定义事务管理器(声明式的事务) --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <!-- 配置DAO --> <bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean id="userDao" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <!-- 配置事务管理器 --> <property name="transactionManager" ref="transactionManager" /> <property name="target" ref="userDaoTarget" /> <property name="proxyInterfaces" value="com.bluesky.spring.dao.GeneratorDao" /> <!-- 配置事务属性 --> <property name="transactionAttributes"> <props> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean> </beans>
第二种方式:全部Bean共享一个代理基类数组
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml" /> <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> </bean> <!-- 定义事务管理器(声明式的事务) --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean id="transactionBase" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" lazy-init="true" abstract="true"> <!-- 配置事务管理器 --> <property name="transactionManager" ref="transactionManager" /> <!-- 配置事务属性 --> <property name="transactionAttributes"> <props> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean> <!-- 配置DAO --> <bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean id="userDao" parent="transactionBase" > <property name="target" ref="userDaoTarget" /> </bean> </beans>
第三种方式:使用拦截器session
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml" /> <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> </bean> <!-- 定义事务管理器(声明式的事务) --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> <property name="transactionManager" ref="transactionManager" /> <!-- 配置事务属性 --> <property name="transactionAttributes"> <props> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean> <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames"> <list> <value>*Dao</value> </list> </property> <property name="interceptorNames"> <list> <value>transactionInterceptor</value> </list> </property> </bean> <!-- 配置DAO --> <bean id="userDao" class="com.bluesky.spring.dao.UserDaoImpl"> <property name="sessionFactory" ref="sessionFactory" /> </bean> </beans>
第四种方式:使用tx标签配置的拦截器dom
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <context:annotation-config /> <context:component-scan base-package="com.bluesky" /> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml" /> <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> </bean> <!-- 定义事务管理器(声明式的事务) --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </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> </beans>
第五种方式:全注解
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <context:annotation-config /> <context:component-scan base-package="com.bluesky" /> <tx:annotation-driven transaction-manager="transactionManager"/> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml" /> <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> </bean> <!-- 定义事务管理器(声明式的事务) --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> </beans>
此时在DAO上需加上@Transactional注解,以下:
package com.bluesky.spring.dao; import java.util.List; import org.hibernate.SessionFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import org.springframework.stereotype.Component; import com.bluesky.spring.domain.User; @Transactional @Component("userDao") public class UserDaoImpl extends HibernateDaoSupport implements UserDao { public List<User> listUsers() { return this.getSession().createQuery("from User").list(); } }
Spring声明式事务让咱们从复杂的事务处理中获得解脱。使得咱们再也无须要去处理得到链接、关闭链接、事务提交和回滚等这些操做。再也无须要咱们在与事务相关的方法中处理大量的try…catch…finally代码。
咱们在使用Spring声明式事务时,有一个很是重要的概念就是事务属性。事务属性一般由事务的传播行为,事务的隔离级别,事务的超时值和事务只读标志组成。咱们在进行事务划分时,须要进行事务定义,也就是配置事务的属性。
Spring在TransactionDefinition接口中定义这些属性,以供PlatfromTransactionManager使用, PlatfromTransactionManager是spring事务管理的核心接口。
1. TransactionDefinition 2. public interface TransactionDefinition { 3. int getPropagationBehavior(); 4. int getIsolationLevel(); 5. int getTimeout(); 6. boolean isReadOnly(); 7. }
getTimeout()方法,它返回事务必须在多少秒内完成。
isReadOnly(),事务是否只读,事务管理器可以根据这个返回值进行优化,确保事务是只读的。
getIsolationLevel()方法返回事务的隔离级别,事务管理器根据它来控制另一个事务能够看到本事务内的哪些数据。
在TransactionDefinition接口中定义了五个不一样的事务隔离级别
ISOLATION_DEFAULT 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.另外四个与JDBC的隔离级别相对应
ISOLATION_READ_UNCOMMITTED 这是事务最低的隔离级别,它充许别外一个事务能够看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。
例如:
Mary的原工资为1000,财务人员将Mary的工资改成了8000,但未提交事务
1. Connection con1 = getConnection();
2. con.setAutoCommit(false);
3. update employee set salary = 8000 where empId ="Mary";
与此同时,Mary正在读取本身的工资
1. Connection con2 = getConnection();
2. select salary from employee where empId ="Mary";
3. con2.commit();
Mary发现本身的工资变为了8000,欢天喜地!
而财务发现操做有误,而回滚了事务,Mary的工资又变为了1000
1. //con1
2. con1.rollback();
像这样,Mary记取的工资数8000是一个脏数据。
ISOLATION_READ_COMMITTED 保证一个事务修改的数据提交后才能被另一个事务读取。另一个事务不能读取该事务未提交的数据。这种事务隔离级别能够避免脏读出现,可是可能会出现不可重复读和幻像读。
ISOLATION_REPEATABLE_READ 这种事务隔离级别能够防止脏读,不可重复读。可是可能出现幻像读。它除了保证一个事务不能读取另外一个事务未提交的数据外,还保证了避免下面的状况产生(不可重复读)。
在事务1中,Mary 读取了本身的工资为1000,操做并无完成
1. con1 = getConnection();
2. select salary from employee empId ="Mary";
在事务2中,这时财务人员修改了Mary的工资为2000,并提交了事务.
1. con2 = getConnection();
2. update employee set salary = 2000;
3. con2.commit();
在事务1中,Mary 再次读取本身的工资时,工资变为了2000
1. //con1
2. select salary from employee empId ="Mary";
在一个事务中先后两次读取的结果并不致,致使了不可重复读。
使用ISOLATION_REPEATABLE_READ能够避免这种状况发生。
ISOLATION_SERIALIZABLE 这是花费最高代价可是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。
目前工资为1000的员工有10人。
事务1,读取全部工资为1000的员工。
1. con1 = getConnection();
2. Select * from employee where salary =1000;
共读取10条记录
这时另外一个事务向employee表插入了一条员工记录,工资也为1000
1. con2 = getConnection();
2. Insert into employee(empId,salary) values("Lili",1000);
3. con2.commit();
事务1再次读取全部工资为1000的员工
1. //con1
2. select * from employee where salary =1000;
共读取到了11条记录,这就产生了幻像读。
ISOLATION_SERIALIZABLE能避免这样的状况发生。可是这样也耗费了最大的资源。
getPropagationBehavior()返回事务的传播行为,由是否有一个活动的事务来决定一个事务调用。
在TransactionDefinition接口中定义了七个事务传播行为。
PROPAGATION_REQUIRED 若是存在一个事务,则支持当前事务。若是没有事务则开启一个新的事务。
1. //事务属性 PROPAGATION_REQUIRED
2. methodA{
3. ……
4. methodB();
5. ……
6. }
7.
8. //事务属性 PROPAGATION_REQUIRED
9. methodB{
10. ……
11.}
使用spring声明式事务,spring使用AOP来支持声明式事务,会根据事务属性,自动在方法调用以前决定是否开启一个事务,并在方法执行以后决定事务提交或回滚事务。
单独调用methodB方法
1. main{
2. metodB();
3. }
至关于
1. Main{
2. Connection con=null;
3.
4. rry{
5. con = getConnection();
6. con.setAutoCommit(false);
7. //方法调用
8. methodB();
9. //提交事务
10.con.commit();
11.}
12.Catch(RuntimeException ex){
13. //回滚事务
14. con.rollback();
15.}
16.finally{
17. //释放资源
18. closeCon();
19.}
20.}
Spring保证在methodB方法中全部的调用都得到到一个相同的链接。在调用methodB时,没有一个存在的事务,因此得到一个新的链接,开启了一个新的事务。
单独调用MethodA时,在MethodA内又会调用MethodB.
执行效果至关于
1. main{
2. Connection con = null;
3. try{
4. con = getConnection();
5. methodA();
6. con.commit();
7. }
8. cathc(RuntimeException ex){
9. con.rollback();
10.}
11.finally{
12. closeCon();
13.}
14.}
调用MethodA时,环境中没有事务,因此开启一个新的事务.
当在MethodA中调用MethodB时,环境中已经有了一个事务,因此methodB就加入当前事务。
PROPAGATION_SUPPORTS 若是存在一个事务,支持当前事务。若是没有事务,则非事务的执行。可是对于事务同步的事务管理器,PROPAGATION_SUPPORTS与不使用事务有少量不一样。
1. //事务属性 PROPAGATION_REQUIRED
2. methodA(){
3. methodB();
4. }
5.
6. //事务属性 PROPAGATION_SUPPORTS
7. methodB(){
8. ……
9. }
单纯的调用methodB时,methodB方法是非事务的执行的。
当调用methdA时,methodB则加入了methodA的事务中,事务地执行。
PROPAGATION_MANDATORY 若是已经存在一个事务,支持当前事务。若是没有一个活动的事务,则抛出异常。
1. //事务属性 PROPAGATION_REQUIRED
2. methodA(){
3. methodB();
4. }
5.
6. //事务属性 PROPAGATION_MANDATORY
7. methodB(){
8. ……
9. }
当单独调用methodB时,由于当前没有一个活动的事务,则会抛出异常
throw new IllegalTransactionStateException("Transactionpropagation 'mandatory' but no existing transaction found");
当调用methodA时,methodB则加入到methodA的事务中,事务地执行。
PROPAGATION_REQUIRES_NEW 老是开启一个新的事务。若是一个事务已经存在,则将这个存在的事务挂起。
1. //事务属性 PROPAGATION_REQUIRED
2. methodA(){
3. doSomeThingA();
4. methodB();
5. doSomeThingB();
6. }
7.
8. //事务属性 PROPAGATION_REQUIRES_NEW
9. methodB(){
10. ……
11.}
当单独调用methodB时,至关于把methodb声明为REQUIRED。开启一个新的事务,事务地执行。
当调用methodA时
1. main(){
2. methodA();
3. }
状况有些大不同.至关于下面的效果。
1. main(){
2. TransactionManager tm = null;
3. try{
4. //得到一个JTA事务管理器
5. tm = getTransactionManager();
6. tm.begin();//开启一个新的事务
7. Transaction ts1 = tm.getTransaction();
8. doSomeThing();
9. tm.suspend();//挂起当前事务
10. try{
11. tm.begin();//从新开启第二个事务
12. Transaction ts2 = tm.getTransaction();
13. methodB();
14. ts2.commit();//提交第二个事务
15.
16. }
17. Catch(RunTimeException ex){
18. ts2.rollback();//回滚第二个事务
19. }
20. finally{
21. //释放资源
22. }
23. //methodB执行完后,复恢第一个事务
24. tm.resume(ts1);
25.doSomeThingB();
26. ts1.commit();//提交第一个事务
27.}
28.catch(RunTimeException ex){
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_SUPPORTED 老是非事务地执行,并挂起任何存在的事务。
1. //事务属性 PROPAGATION_REQUIRED
2. methodA(){
3. doSomeThingA();
4. methodB();
5. doSomeThingB();
6. }
7.
8. //事务属性 PROPAGATION_NOT_SUPPORTED
9. methodB(){
10. ……
11.}
当单独调用methodB时,不启用任何事务机制,非事务地执行。
当调用methodA时,至关于下面的效果
1. main(){
2. TransactionManager tm = null;
3. try{
4. //得到一个JTA事务管理器
5. tm = getTransactionManager();
6. tm.begin();//开启一个新的事务
7. Transaction ts1 = tm.getTransaction();
8. doSomeThing();
9. tm.suspend();//挂起当前事务
10. methodB();
11. //methodB执行完后,复恢第一个事务
12. tm.resume(ts1);
13.doSomeThingB();
14. ts1.commit();//提交第一个事务
15.}
16.catch(RunTimeException ex){
17. ts1.rollback();//回滚第一个事务
18.}
19.finally{
20. //释放资源
21.}
22.}
使用PROPAGATION_NOT_SUPPORTED,也须要使用JtaTransactionManager做为事务管理器。
PROPAGATION_NEVER 老是非事务地执行,若是存在一个活动事务,则抛出异常
1. //事务属性 PROPAGATION_REQUIRED
2. methodA(){
3. doSomeThingA();
4. methodB();
5. doSomeThingB();
6. }
7.
8. //事务属性 PROPAGATION_NEVER
9. methodB(){
10. ……
11.}
单独调用methodB,则非事务的执行。
调用methodA则会抛出异常
throw new IllegalTransactionStateException(
"Transaction propagation 'never' butexisting transaction found");
PROPAGATION_NESTED若是一个活动的事务存在,则运行在一个嵌套的事务中. 若是没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行
这是一个嵌套事务,使用JDBC 3.0驱动时,仅仅支持DataSourceTransactionManager做为事务管理器。须要JDBC 驱动的java.sql.Savepoint类。有一些JTA的事务管理器实现可能也提供了一样的功能。
使用PROPAGATION_NESTED,还须要把PlatformTransactionManager的nestedTransactionAllowed属性设为true;
而nestedTransactionAllowed属性值默认为false;
1. //事务属性 PROPAGATION_REQUIRED
2. methodA(){
3. doSomeThingA();
4. methodB();
5. doSomeThingB();
6. }
7.
8. //事务属性 PROPAGATION_NESTED
9. methodB(){
10. ……
11.}
若是单独调用methodB方法,则按REQUIRED属性执行。
若是调用methodA方法,至关于下面的效果
1. main(){
2. Connection con = null;
3. Savepoint savepoint = null;
4. try{
5. con = getConnection();
6. con.setAutoCommit(false);
7. doSomeThingA();
8. savepoint = con2.setSavepoint();
9. try
10. methodB();
11. }catch(RuntimeException ex){
12. con.rollback(savepoint);
13. }
14. finally{
15. //释放资源
16. }
17.
18. doSomeThingB();
19. con.commit();
20.}
21.catch(RuntimeException ex){
22. con.rollback();
23.}
24.finally{
25. //释放资源
26.}
27.}
当methodB方法调用以前,调用setSavepoint方法,保存当前的状态到savepoint。若是methodB方法调用失败,则恢复到以前保存的状态。可是须要注意的是,这时的事务并无进行提交,若是后续的代码(doSomeThingB()方法)调用失败,则回滚包括methodB方法的全部操做。
嵌套事务一个很是重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所作的动做。而内层事务操做失败并不会引发外层事务的回滚。
PROPAGATION_NESTED与PROPAGATION_REQUIRES_NEW的区别:它们很是相似,都像一个嵌套事务,若是不存在一个活动的事务,都会开启一个新的事务。使用PROPAGATION_REQUIRES_NEW时,内层事务与外层事务就像两个独立的事务同样,一旦内层事务进行了提交后,外层事务不能对其进行回滚。两个事务互不影响。两个事务不是一个真正的嵌套事务。同时它须要JTA事务管理器的支持。
使用PROPAGATION_NESTED时,外层事务的回滚能够引发内层事务的回滚。而内层事务的异常并不会致使外层事务的回滚,它是一个真正的嵌套事务。DataSourceTransactionManager使用savepoint支持PROPAGATION_NESTED时,须要JDBC 3.0以上驱动及1.4以上的JDK版本支持。其它的JTA TrasactionManager实现可能有不一样的支持方式。
PROPAGATION_REQUIRED应该是咱们首先的事务传播行为。它可以知足咱们大多数的事务需求。
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = { AppBizExeA.class } , noRollbackFor = { AppBizExeB.class }) public void method1() throws Exception { System.out.println("method1 start"); TPerson per = new TPerson(); per.setAge("24"); per.setId(123); per.setName("xj"); personDao.add(per); throw new NullPointerException(); } @Transactional(propagation = Propagation.NESTED, rollbackFor = { AppBizExeA.class }) public void method2() throws Exception { System.out.println("method2 start"); TPerson per = new TPerson(); per.setAge("24"); per.setId(1234); per.setName("xj"); personDao.add(per); System.out.println("method2 end"); }
1. public static void main(String[] args) throws Exception {
2.
3. ApplicationContext context = new ClassPathXmlApplicationContext(new String[] { "classpath:com/benx/benx.xml" });
4. PersonService service = (PersonService) context.getBean("personService");
5. service.method1();
6.
7. }
今天只谈传播行为为REQUIRED的,其余先不谈,由于使用比较少,并且支持也不够好
一、执行service.method1(),无论method里面是否嵌套了method2或其余,事务都是以method1开始和结尾,且事务配置一样以method1为准,忽略其余REQUIRED行为的配置,好比异常机制,以method1为准,忽略method2的异常配置
二、Transactional的异常控制,默认是Check Exception 不回滚,unCheck Exception回滚
三、若是配置了rollbackFor 和 noRollbackFor 且两个都是用一样的异常,那么遇到该异常,仍是回滚
四、rollbackFor 和noRollbackFor 配置也许不会含盖全部异常,对于遗漏的按照Check Exception 不回滚,unCheck Exception回滚
@Transactional只能被应用到public方法上, 对于其它非public的方法,若是标记了@Transactional也不会报错,但方法没有事务功能.
Spring使用声明式事务处理,默认状况下,若是被注解的数据库操做方法中发生了unchecked异常,全部的数据库操做将rollback;若是发生的异常是checked异常,默认状况下数据库操做仍是会提交的。
这种默认的行为是能够改变的。
使用@Transactional注解的noRollbackFor和rollbackFor属性
如:@Transactional(rollbackFor=Exception.class)可使checked异常发生时,数据库操做也rollback、@Transactional(noRollbackFor=RuntimeException.class)可使unchecked异常发生时也提交数据库操做。
也可使用noRollbackForClassName、rollbackForClassName属性来指定一个异常类名的String数组来改变默认的行为。
读取数据库中的数据时是不须要事务管理的,这种状况下可使用事务的传播行为来告诉Spring不须要开启事务,
如:@Transactional(propagation =Propagation.NOT_SUPPORTED)。
事务的传播行为有:
1. REQUIRED:表示业务方法须要在一个事务中处理,若是业务方法执行时已经在一个事务中,则加入该事务,不然从新开启一个事务。这也是默认的事务传播行为;
2. NOT_SUPPORTED:声明业务方法不须要事务,若是业务方法执行时已经在一个事务中,则事务被挂起,等方法执行完毕后,事务恢复进行;
3. REQUIRES_NEW:代表业务方法须要在一个单独的事务中进行,若是业务方法进行时已经在一个事务中,则这个事务被挂起,并从新开启一个事务来执行这个业务方法,业务方法执行完毕后,原来的事务恢复进行;
4. MANDATORY:该属性指定业务方法只能在一个已经存在的事务中进行,业务方法不能发起本身的事务;若是业务方法没有在一个既有的事务中进行,容器将抛出异常;
5. SUPPORTS:该属性指定,若是业务方法在一个既有的事务中进行,则加入该事务;不然,业务方法将在一个没有事务的环境下进行;
6. NEVER:指定业务方法不能够在事务中进行,若是业务方法执行时已经在一个事务中,容器将抛出异常;
7. NESTED:该属性指定,若是业务方法在一个既有的事务中执行,则该业务方法将在一个嵌套的事务中进行;不然,按照REQUEIRED来对待。它使用一个单独的事务,这个事务能够有多个rollback点,内部事务的rollback对外部事务没有影响,但外部事务的rollback会致使内部事务的rollback。这个行为只对DataSourceTransactionManager有效。
事务的隔离级别
使用@Transactional的Isolation属性能够指定事务的隔离级别。但事务的隔离级别是由底层的数据库实现的,并非由Spring来实现。
1. READ_UNCOMMITTED:会出现脏读、不可重复读和幻读问题;
2. READ_COMMITTED:会出现不可重复读和幻读问题;
3. REPEATABLE_READ:会出现幻读问题;
4. SERIALIZABLE:串行化,不会出现上面的问题。
通常的数据库默认提供的是READ_COMMITTED隔离级别,如sqlserver2000;Mysql默认提供的是REPEATABLE_READ。
@Transactional 的全部可选属性以下:
属性 类型 默认值 说明
propagation Propagation枚举 REQUIRED 事务传播属性
isolation isolation枚举 DEFAULT 事务隔离级别
readOnly boolean false 是否只读
timeout int -1 超时(秒)
rollbackFor Class[] {} 须要回滚的异常类
rollbackForClassName String[] {} 须要回滚的异常类名
noRollbackFor Class[] {} 不须要回滚的异常类
noRollbackForClassName String[] {} 不须要回滚的异常类名
//事务传播属性
@Transactional(propagation=Propagation.REQUIRED) //若是有事务,那么加入事务,没有的话新建一个(不写的状况下)
@Transactional(propagation=Propagation.NOT_SUPPORTED) //容器不为这个方法开启事务
@Transactional(propagation=Propagation.REQUIRES_NEW) //不论是否存在事务,都建立一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.MANDATORY) //必须在一个已有的事务中执行,不然抛出异常
@Transactional(propagation=Propagation.NEVER) //必须在一个没有的事务中执行,不然抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS) //若是其余bean调用这个方法,在其余bean中声明事务,那就用事务.若是其余bean没有声明事务,那就不用事务.
@Transactional(propagation=Propagation.NESTED)
@Transactional (propagation =Propagation.REQUIRED,readOnly=true) //readOnly=true只读,不能更新,删除
@Transactional (propagation =Propagation.REQUIRED,timeout=30)//设置超时时间
@Transactional (propagation =Propagation.REQUIRED,isolation=Isolation.DEFAULT)//设置数据库隔离级别
用 spring 事务管理器,由spring来负责数据库的打开,提交,回滚.
默认遇到运行期例外(throw new RuntimeException("注释");)会回滚,即遇到不受检查(unchecked)的例外时回滚;
而遇到须要捕获的例外(throw new Exception("注释");)不会回滚,即遇到受检查的例外(就是非运行时抛出的异常,编译器会检查到的异常叫受检查例外或说受检查异常)时,需咱们指定方式来让事务回滚 ,以下:
@Transactional(rollbackFor=Exception.class) //指定回滚,遇到异常Exception时回滚
public void methodName() {
throw new Exception("注释");
}
@Transactional(noRollbackFor=Exception.class)//指定不回滚,遇到运行期例外(throw newRuntimeException("注释");)会回滚
public ItimDaoImpl getItemDaoImpl() {
throw new RuntimeException("注释");
}