Spring 事务详解

来源:互联网 发布:华为mate9网络频段 编辑:程序博客网 时间:2024/06/11 08:29
1 事务的回顾
(1) 什么是事务:
     事务逻辑上的一组操作,组成这组操作的各个逻辑单元,要么一起成功,要么一起失败
(2) 事务特性:
     原子性:强调事务的不可分割.
     一致性:事务的执行的前后数据的完整性保持一致.
     隔离性:一个事务执行的过程中,不应该受其他事务的干扰.
     持久性:事务一旦结束,数据就持久到数据库
(3) 如果不考虑隔离性引发安全性问题:
     脏读:一个事务读到另一个事务的未提交的数据
     不可重复读:一个事务读到另一个事务已经提交的update的数据导致多次查询结果不一致.
     虚幻读:一个事务读到另一个事务已经提交的insert的数据导致多次查询结果不一致.
(4) 解决读问题:设置事务隔离级别
     未提交读:脏读,不可重复读,虚读都有可能发生
     已提交读:避免脏读。但是不可重复读和虚读有可能发生
     可重复读:避免脏读和不可重复读.但是虚读有可能发生.
     串行化的:避免以上所有读问题.
(5) MySQL默认:可重复读
      Oracle默认:读已提交

2 Spring 进行事物管理一组API
     Spring 进行事物管理主要分为编程式事务和声明式事务
(1) 编程式事务:编程式事务需要你在代码中直接加入处理事务的逻辑,可能需要在代码中显式调用beginTransaction()、commit()、rollback()等事务管理相关的方法,如在执行a方法时候需要事务处理,你需要在a方法开始时候开启事务,处理完后。在方法结束时候,关闭事务.在实际项目开发当中一般不使用。

(2) 声明式事务:声明式的事务的做法是在a方法外围添加注解或者直接在配置文件中定义,a方法需要事务处理,在spring中会通过配置文件在a方法前后拦截,并添加事务.
     ① 基于注解方式的声明式事务
     ② 基于XML方式的声明式事务

基于注解方式的声明式事务
(1) 增加相关配置:增加tx名称空间
<?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/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd          http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd          http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">     <!-- 扫描注解的包 -->     <context:component-scan base-package="com.atguigu.spring.*"/>     <!-- 加载外部资源文件 -->     <context:property-placeholder location="classpath:jdbc.properties"/>     <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">          <property name="user" value="${jdbc.user}"/>          <property name="password" value="${jdbc.password}"/>          <property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>          <property name="driverClass" value="${jdbc.driverClass}"/>          <property name="initialPoolSize" value="${jdbc.initialPoolSize}"/>          <property name="minPoolSize" value="${jdbc.minPoolSize}"/>          <property name="maxPoolSize" value="${jdbc.maxPoolSize}"/>          <property name="acquireIncrement" value="${jdbc.acquireIncrement}"/>          <property name="maxStatements" value="${jdbc.maxStatements}"/>          <property name="maxStatementsPerConnection" value="${jdbc.maxStatementsPerConnection}"/>     </bean>     <!-- 声明JdbcTemplate -->     <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">          <property name="dataSource" ref="dataSource"></property>     </bean>     <!-- 事务管理器:用于完成事务的开启,提交,回滚 -->     <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">          <property name="dataSource" ref="dataSource"></property>     </bean>     <!-- 开启基于注解的声明式事务           transaction-manager="transactionManager" 默认值。      -->     <tx:annotation-driven transaction-manager="transactionManager"/></beans>
(2) 在业务层方法上使用事务注解增加事务
@Service("bookShopService")public class BookShopServiceImpl implements BookShopService {     @Autowired     private BookShopDao bookShopDao ;     //1.测试没有事务情况(正常运行)     //2.测试没有事务情况(非正常运行:数据操作失败,有的语句执行成功了,有的语句失败,没有保证数据一致性和完整性。)     //3.测试有事务情况 :@Transactional     @Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.REPEATABLE_READ,                   rollbackFor={java.lang.Exception.class},                   timeout=3,readOnly=true)     public void purchase(String isbn, String username) throws Exception {          int price = bookShopDao.findBookPriceByIsbn("ISBN-001");          int price2 = bookShopDao.findBookPriceByIsbn("ISBN-001");     }     @Transactional(propagation=Propagation.REQUIRED)     public void updatePrice(String isbn, int price) throws Exception {          bookShopDao.updateBookPrice(isbn,price);     }}
     propagation:事务的传播行为;
     isolation:事务的隔离级别设置;
     rollbackFor:事务的异常处理;
     timeout:事务的超时属性;
     readOnly:事务的只读属性设置;
(3) 框架如何创建代理对象,实现事务的功能扩展
     目标对象有接口采用JDK动态代理, 如果目标对象没有接口,会采用Cglib动态代理。
(4) 事务的属性
① 事务的传播行为propagation :a() -> b():a()方法调用b()方法,b()方法的事务传播行为:
     REQUIRED :
如果a()方法没有事务,那么,b()方法要开启一个新的事务
如果a()方法有事务,那么,b()方法要加入a()方法事务中。
     REQUIRES_NEW:
         如果a()方法没有事务,那么,b()方法要开启一个新的事务。
         如果a()方法有事务,那么b()方法也要为自己开启一个新的事务,当b()方法运行时,a()方法事务要挂起,直到b()方法事务结束后,a()方法事务继续执行。
②事务的隔离级别 isolation: 1,2,4,8 数据库定义的隔离级别。
  

③事务的回滚策略
     Spring框架遇到异常时,并不是所有的异常都进行事务回滚。
     如果遇到Exception或Error不会进行事务自动回滚;如果是RuntimeException异常,默认进行回滚。
     如果希望存在异常就进行回滚,那么可以修改异常回滚策略:
rollbackFor={java.lang.Exception.class}  指定哪些异常类型需要回滚事务noRollbackFor={} 指定哪些异常类型不需要回滚。方法中如果自己处理了异常,为了让事务得到回滚,必须要将异常抛给框架。否则,不会进行事务回滚。
④事务的超时属性
     如果事务执行过程中,超出一定的时间,需要将事务结束掉,可以设置超时属性值。
     目的,事务执行时间过长,会对其他事务操作数据有影响。系统性能降低。
⑤事务的只读属性
     如果是做数据查询操作,而不是数据的修改(insert,update,delete),可以不进行事务控制。
     为了提高数据的访问效率,可以设置只读操作。让数据库对只读操作进行性能的优化,提高数据的访问效率。

基于XML方式的声明式事务
<?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/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd          http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd          http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">     <!-- 加载外部资源文件 -->     <context:property-placeholder location="classpath:jdbc.properties"/>     <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">          <property name="user" value="${jdbc.user}"/>          <property name="password" value="${jdbc.password}"/>          <property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>          <property name="driverClass" value="${jdbc.driverClass}"/>          <property name="initialPoolSize" value="${jdbc.initialPoolSize}"/>          <property name="minPoolSize" value="${jdbc.minPoolSize}"/>          <property name="maxPoolSize" value="${jdbc.maxPoolSize}"/>          <property name="acquireIncrement" value="${jdbc.acquireIncrement}"/>          <property name="maxStatements" value="${jdbc.maxStatements}"/>          <property name="maxStatementsPerConnection" value="${jdbc.maxStatementsPerConnection}"/>     </bean>     <!-- 声明JdbcTemplate -->     <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">          <property name="dataSource" ref="dataSource"></property>     </bean>     <!-- 事务管理器:用于完成事务的开启,提交,回滚 -->     <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">          <property name="dataSource" ref="dataSource"></property>     </bean>     <bean id="bookShopDao" class="com.atguigu.spring.dao.BookShopDaoImpl">          <property name="jdbcTemplate" ref="jdbcTemplate"></property>     </bean>     <!-- 目标对象 -->     <bean id="bookShopService" class="com.atguigu.spring.service.BookShopServiceImpl">          <property name="bookShopDao" ref="bookShopDao"></property>     </bean>     <!-- 事务与业务层对象之间的关系 -->     <aop:config>          <!-- 声明切入点 -->          <aop:pointcut expression="execution(* com.atguigu..service.*.*(..))" id="pointcutID"/>          <!-- 声明切入点与通知之间的关系 -->          <aop:advisor advice-ref="txAdviceID" pointcut-ref="pointcutID"/>     </aop:config>     <!-- 声明通知 -->     <tx:advice id="txAdviceID" transaction-manager="transactionManager">          <tx:attributes>               <tx:method name="purchase" propagation="REQUIRED"                         isolation="DEFAULT" rollback-for="java.lang.Exception" timeout="3"/>               <tx:method name="update*" propagation="REQUIRED"                         isolation="DEFAULT" rollback-for="java.lang.Exception" timeout="3"/>               <tx:method name="add*" propagation="REQUIRED"                         isolation="DEFAULT" rollback-for="java.lang.Exception" timeout="3"/>               <tx:method name="delete*" propagation="REQUIRED"                         isolation="DEFAULT" rollback-for="java.lang.Exception" timeout="3"/>               <tx:method name="query*" read-only="true"/>               <tx:method name="get*" read-only="true"/>          </tx:attributes>     </tx:advice></beans>