Spring 有一整套的事务管理机制,开发人员在使用的时候不用了解在在底层是怎么实现的。可以使用这样的事务机制,处理各种情况下的事务处理
PART ONE:注解实现事务
在spring中不同的操作有不同的事务管理器
JDBC事务管理器: DataSourceTransactionManager
Hibernate 对应的事务管理器:HIbernateTransactionManager
...
一:完成相关的XML配置
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--启用事务注解-->
<tx:annotation-driven transaction-manager="transactionManager" />
二:使用事务注解 @Transactional
//在Service 层的方法上添加注解@Trasactional,就可以实现事务管理
@Transactional
public void Test(){...}
PART TWO
一:事务的传播行为
当一个事务方法中调用另一个事务时,必须指定事务该如何传播;在注解@Transactional 的属性中 propagation 进行指定
/*例如,买书作为一个功能,要完成的数据库操作是:书本的数量-1,自己的钱减少。这是一个事务方法*/
@Transactional
public void buyBook(){...}
/* 现在一个人要买两本书,这也是一个事务方法,并且这个方法中要调用buyBook*/
@Transactional
public void buyBooks(){
buyBook();
buyBook();
}
/**
* 现在一本书10 但是只有15 块钱,执行的结果会是一本书都没有买到
* 原因: Transaction 的默认传播属性 propagation=Propagation.REQUIRED
-
Transactional的默认传播属性
propagation=Propagation.REQUIRED
表示:使用调用方法的事务(也可以看成:忽视了被调用方法的事务声明) -
Transactional的传播属性
propagation=Propagation.REQUIRES_NEW
表示:该方法必须开启一个新的事务,并在自己的事务中运行,如果当前被调用事务正在运行,那么调用方法的事务要挂起
@Transactional(propagation =Propagation.REQUIRES_NEW)
public void buyBooks(){
buyBook();
buyBook();
}
/* 现在一本书10 但是只有15 块钱,执行的结果会是买到了一本书,第二本书没有买到
*buyBooks 事务开始,buybook 事务开始,buyBooks 事务挂起;buyBook 事务结束,buyBooks 事务继续,buyBook 事务开始,因为钱不够,buyBook 事务回滚,回滚到当前的buyBook 事务开始的地方,buyBook 事务结束,buyBooks 事务开始
* 知道buyBooks 运行结束 buyBooks 事务结束
* buyBooks 本身时有事务的,因为事务传播属性propagation = Propagation.REQUIRES_NEW 会在buyBooks 事务中新开一个事务buyBook 并且在buyBooks 事务中运行
*/
二:事务的隔离级别
并发事务所导致的问题,当一个或多个应用程序中的多个事务在同一个数据集上并发执行时,可能会发生许多的意外问题(脏读,不可重复读,幻读)
使用@Transactional 的属性 isolation 指定提交级别 isolation=Isolation.RED_COMMITTED
读已提交
三: 事务的回滚属性
Spring 默认情况下对所有的运行时异常进行回滚,也可以通过属性的是设置,指定对那些异常进行回滚,对那些异常不进行回滚
/*涉及的属性*/
对那些异常进行回滚 rollbackFor={异常.class} rollbackForClassName
noRollbackFor noRollbackForClassName
/*一般不会对这个属性进行设置,使用默认的出现运行时异常就进行回滚*/
四:使用readyOnly
设置 readOnly 属性 readOnly = true
表示这个事务只读取数据不糊更新数据,这样可以帮助数据库引擎优化事务;当这个事务确实为一个只读属性的时候就要进行这样的设置
五: 事务过期
设置 timeout 属性,timeout= 秒数
,事务的执行之间超过Timeout 的设置时间,就会被强制回滚(尽管没有异常)
在买书的buyBook 事务中添加代码,进行测试
@Transactional(timeout=2)
public void buyBook(){
try{
Thread.sleep(4000)
}catch(Exception ex){...}
/*此时你买一本书,你还有15块钱,你也无法购买成功,因为,设置的最长时间是2s 但是函数中设置了睡眠时间4s 事务的存在时间超过了2s 就会被强制回滚*/
/* 好处在于: 在这种情况下 减少事务的占用时间*/
PART THREE : 使用XML配置文件的方式配置事务
首先配置需要的bean
配置事务管理器
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
配置事务属性
<!--1.配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--2.配置事务属性-->
<tx:advice transaction-manager="transactionManager" id="txAdvice">
<tx:attributes>
<tx:method name="TestTwo" propagation="REQUIRES_NEW" />
<tx:method name="方法名" 相应的属性配置 />
<!--get* 表示所有的getxxx() 方法-->
<tx:method name="get*" readOnly="true" />
<!--这里可以在那些方法上进行配置属性,完全看事务的切入点;如果事务的切入点是一个方法,那么这里肯定就只能对哪一个那一个方法进行属性配置-->
</tx:attributes>
</tx:advice>
<!--3.配置事务切点,以及把事务切入点和事务属性关联起来-->
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* spring.AOP.Test.jdbcTemplateTest.TestTwo(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
</aop:config>