Spring事务看这个肯定会

Spring的AOP配置

    1.第一种:注解配置AOP

    2.第二种:xml配置AOP

 

使用Spring AOP实现声明式事务管理

1.基于XML配置,现在也有很多通过注解的方式来配置事务管理类

(1)配置事务管理类

 

[html] view plain copy

  1.  <!-- 定义事务管理器 -->    
  2. <bean id="transactionManager"    
  3.     class="org.springframework.jdbc.datasource.DataSourceTransactionManager">    
  4.     <property name="dataSource" ref="dataSource" />    
  5. </bean>    

在spring的配置中配置数据源(dataSource)、事务管理器,事务管理器使用不同的orm框架事务管理器类就不同,mybatis 是org.springframework.jdbc.datasource.DataSourceTransactionManager  。而hibernate事务管理器为org.springframework.orm.hibernate3.HibernateTransactionManager  

   或者通过注解配置事务管理类

   @Bean

    public LocalContainerEntityManagerFactoryBean entityManagerFactory(){

    }

    @Bean
   public PlatformTransactionManager transactionManager() {
    LocalContainerEntityManagerFactoryBean factoryBean = entityManagerFactory();
        if (factoryBean != null) {
        return new JpaTransactionManager(factoryBean.getObject());
        }
    return null;
   }   

(2)配置事务属性

[html] view plain copy

  1. <!-- 配置事务的属性 -->    
  2.     <tx:advice id="TestAdvice" transaction-manager="transactionManager">    
  3.         <!--配置事务传播性,隔离级别以及超时回滚等问题 -->    
  4.         <tx:attributes>    
  5.             <tx:method name="search*" propagation="REQUIRED" read-only="true" isolation="DEFAUT" TIMEOUT="-1" />    
  6.             <tx:method name="del*" propagation="REQUIRED" />    
  7.             <tx:method name="update*" propagation="REQUIRED" />    
  8.             <tx:method name="add*" propagation="REQUIRED" />    
  9.         </tx:attributes>    
  10.     </tx:advice>  

事务属性在<tx:method>中进行设置,Spring支持对不同的方法设置不同的事务属性,所以可以为一个<tx:advice>设置多个<tx:method>,其中name属性指定匹配的方法(这里需要对这些方法名进行约定,如果事务切入点在service上,则最好和Dao的方法命名区分开,也不要使用get set关键字,防止和属性的getter setter发生混淆)

事务有以下几个常用属性:

a.read-only:设置该事务中是否允许修改数据。(对于只执行查询功能的事务,设置为TRUE可以提高事务的执行速度)  

b.propagation:事务的传播机制。一般设置为required。可以保证在事务中的代码只在当前事务中运行,防止创建多个事务。

c.isolation:事务隔离级别。不是必须的。默认值是default。

d.timeout:允许事务运行的最长时间,以秒为单位。

e.rollback-for:触发回滚的异常。

f.no-rollback-for:不会触发回滚的异常。

Table 9.1. <tx:method/> 有关的设置

 

属性 是否需要? 默认值 描述
name  

与事务属性关联的方法名。通配符(*)可以用来指定一批关联到相同的事务属性的方法。 如:'get*'、'handle*'、'on*Event'等等。

propagation REQUIRED 事务传播行为
isolation DEFAULT 事务隔离级别
timeout -1 事务超时的时间(以秒为单位)
read-only false 事务是否只读?
rollback-for  

将被触发进行回滚的 Exception(s);以逗号分开。 如:'com.foo.MyBusinessException,ServletException'

no-rollback-for  

 被触发进行回滚的 Exception(s);以逗号分开。 如:'com.foo.MyBusinessException,ServletException'

***实际开发中,对于只执行查询功能的事务,要设置read-only为TRUE,其他属性一般使用默认值即可。

(3)配置事务的AOP切入点

[html] view plain copy

  1. <aop:config>    
  2.         <!--配置事务切点 -->    
  3.         <aop:pointcut id="services"    
  4.             expression="execution(public* com.pb.service.*.*(..))" />    
  5.         <aop:advisor pointcut-ref="services" advice-ref="TestAdvice" />    
  6.     </aop:config>    

 

该设置的含义是:对于com.pb.service.impl包及子包下的所有类的所有公共方法进行切入。(被切入的 方法经过<tx:method>筛选)web应用程序最合适的事务切入点是Service的方法上。

----通过以上三个步骤设置好声明式事务后,当Service中 的业务方法被调用之前,Spring会获取事务对象并启动事务。并使用try-catch-finally来处理异常。业务方法执行成功则会提交事务,默认情况下如果抛出了RuntimeException 或者Rrror 对象就会回滚事务。(注意: 这里注意一下,在tx:method中配置了rollback_for 中配置的Exception 这个是运行时的异常才会回滚不然其他异常是不会回滚的!)

2.使用annotation配置

*1.在事务管理的dao实现类之前标注@Transactional

*2.在要进行事务管理的方法前加上@Transactional(propagation= Propagation.REQUIRED)

*3.在配置文件中指定驱动:<tx:annotation-driven transaction-manager="transactionManager" />

 

[html] view plain copy

  1. package demo.spring.dao;  
  2.   
  3. import java.util.Iterator;  
  4. import java.util.List;  
  5.   
  6. import javax.sql.DataSource;  
  7.   
  8. import org.springframework.jdbc.core.JdbcTemplate;  
  9. import org.springframework.transaction.annotation.Propagation;  
  10. import org.springframework.transaction.annotation.Transactional;  
  11.   
  12. import demo.spring.entity.Person;  
  13. @Transactional//将此类进行事务管理  
  14. public class PersonDaoImpl implements PersonDao {  
  15.     private JdbcTemplate jt;  
  16.       
  17.     public void setDataSource(DataSource dataSource){  
  18.         jt = new JdbcTemplate(dataSource);  
  19.     }  
  20.     @Override  
  21.     public void insert(long id, String name, int age) {  
  22.         jt.update("insert into person values('"+id+"','"+name+"','"+age+"')");  
  23.   
  24.     }  
  25.   
  26.     @Transactional(propagation= Propagation.REQUIRED)//定义要事务管理的方法,指定传播行为  
  27.     public void batchInsert(List persons) {  
  28.         for(Iterator it = persons.iterator(); it.hasNext(); ){  
  29.             Person p = (Person) it.next();  
  30.             insert(p.getId(),p.getName(),p.getAge());  
  31.         }  
  32.     }  
  33.   
  34. }  
  35.  

以上内容转载于:https://blog.csdn.net/c_w_d/article/details/63252340

 

思考:在配置文件中对com.pb.service.impl包及子包下的所有类的所有公共方法进行切入,但是在这个包下面的要进行事务管理的方法没有加@Transactional(propagation= Propagation.REQUIRED),还能对该方法进行事务管理吗?

Spring中常用事务的传播机制(事务类型):

  • PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
  • 解释如下:1、如果ServiceA.methodA已经起了事务,这时调用ServiceB.methodB,ServiceB.methodB看到自己已经运行在ServiceA.methodA的事务内部,就不再起新的事务。这时只有外部事务并且他们是共用的,所以这时ServiceA.methodA或者ServiceB.methodB无论哪个发生异常methodA和methodB作为一个整体都将一起回滚。
      2、如果ServiceA.methodA没有事务,ServiceB.methodB就会为自己分配一个事务。这样,在ServiceA.methodA中是没有事务控制的。只是在ServiceB.methodB内的任何地方出现异常,ServiceB.methodB将会被回滚,不会有ServiceA.methodA的回滚
  • PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
  • PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
  • PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
  • 解释如下:这个就比较绕口了。 比如我们设计ServiceA.methodA的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB的事务级别为PROPAGATION_REQUIRES_NEW,那么当执行到ServiceB.methodB的时候,ServiceA.methodA所在的事务就会挂起,ServiceB.methodB会起一个新的事务,等待ServiceB.methodB的事务完成以后,他才继续执行。他与PROPAGATION_REQUIRED 的事务区别在于事务的回滚程度了。因为ServiceB.methodB是新起一个事务,那么就是存在两个不同的事务。如果ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚,ServiceB.methodB是不会回滚的。如果ServiceB.methodB失败回滚,如果他抛出的异常被ServiceA.methodA捕获,ServiceA.methodA事务仍然可能提交。
  • PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • 解释如下:比如ServiceA.methodA的事务级别是PROPAGATION_REQUIRED ,而ServiceB.methodB的事务级别是PROPAGATION_NOT_SUPPORTED ,
    那么当执行到ServiceB.methodB时,ServiceA.methodA的事务挂起,而他以非事务的状态运行完,再继续ServiceA.methodA的事务。
  • PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
  • 解释如下:假设ServiceA.methodA的事务级别是PROPAGATION_REQUIRED, 而ServiceB.methodB的事务级别是PROPAGATION_NEVER ,
    那么ServiceB.methodB就要抛出异常了。
  • PROPAGATION_REQUIRED类似的操作。
  • PROPAGATION_NESTED--如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。

       解释如下: 理解Nested的关键是savepoint。他与PROPAGATION_REQUIRES_NEW的区别是,PROPAGATION_REQUIRES_NEW另起一个事务,将会与    他的父事务相互独立,而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,如果父事务最后回滚,    他也要回滚的。而Nested事务的好处是他有一个savepoint。

*****************************************
ServiceA {   
       
       
     void methodA() {   
         try {
      //savepoint   
             ServiceB.methodB();    //PROPAGATION_NESTED 级别
         } catch (SomeException) {   
             // 执行其他业务, 如 ServiceC.methodC();   
         }   
     }   
  
}   
********************************************
也就是说ServiceB.methodB失败回滚,那么ServiceA.methodA也会回滚到savepoint点上,ServiceA.methodA可以选择另外一个分支,比如
ServiceC.methodC,继续执行,来尝试完成自己的事务。

 

Spring中常用事务的隔离级别:

  1. ISOLATION_DEFAULT:用底层数据库的默认隔离级别,数据库管理员设置什么就是什么
  2. ISOLATION_READ_UNCOMMITTED(未提交读):最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读)
  3. ISOLATION_READ_COMMITTED(提交读):一个事务提交后才能被其他事务读取到(该隔离级别禁止其他事务读取到未提交事务的数据、所以还是会造成幻读、不可重复读)、sql server默认级别
  4. ISOLATION_REPEATABLE_READ(可重复读):可重复读,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据(该隔离基本可防止脏读,不可重复读(重点在修改),但会出现幻读(重点在增加与删除))(MySql默认级别,更改可通过set transaction isolation level 级别)
  5. ISOLATION_SERIALIZABLE(序列化):代价最高最可靠的隔离级别(该隔离级别能防止脏读、不可重复读、幻读)
    1. 丢失更新:两个事务同时更新一行数据,最后一个事务的更新会覆盖掉第一个事务的更新,从而导致第一个事务更新的数据丢失,这是由于没有加锁造成的;
    2. 幻读:同样的事务操作过程中,不同时间段多次(不同事务)读取同一数据,读取到的内容不一致(一般是行数变多或变少)。
    3. 脏读:一个事务读取到另外一个未提及事务的内容,即为脏读。
    4. 不可重复读:同一事务中,多次读取内容不一致(一般行数不变,而内容变了)。

幻读与不可重复读的区别:幻读的重点在于插入与删除,即第二次查询会发现比第一次查询数据变少或者变多了,以至于给人一种幻象一样,而不可重复读重点在于修改,即第二次查询会发现查询结果比第一次查询结果不一致,即第一次结果已经不可重现了。

数据库隔离级别越高,执行代价越高,并发执行能力越差,因此在实际项目开发使用时要综合考虑,为了考虑并发性能一般使用提交读隔离级别,它能避免丢失更新和脏读,尽管不可重复读和幻读不能避免,但可以在可能出现的场合使用悲观锁或乐观锁来解决这些问题。

悲观锁与乐观锁可参考:http://blog.csdn.net/liaohaojian/article/details/62416972

 

 

第三部分

关于AOP切入点pointcut中的expression的理解

  1.  <aop:pointcut id="services"    
  2.             expression="execution(public* com.pb.service.*.*(..))" />  
  3.  

 

execution(* com.spring.aop.*.*(..))

这是 com.spring.aop包下所有的类的所有方法。。

第一个*代表所有的返回值类型

第二个*代表所有的类

第三个*代表类所有方法

最后一个..代表所有的参数。

 

网上其它示例1:
<aop:pointcut id="serviceMethod" expression="execution(* *.*Service.*(..))" />

第一个* 表示任意返回值类型
第二个* 表示以任意名字开头的package. 如 com.xx.
第三个* 表示 通配 *service下的任意类

第四个* 表示 以任意名字开头的方法名 

最后二个.. 表示通配 方法可以有0个或多个参数

 

部分转载于:https://blog.csdn.net/qq525099302/article/details/53996344,

https://blog.csdn.net/wwh578867817/article/details/51736723

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章