事务的特性:一致性、原子性、隔离性、持久性
Spring事务管理相关接口:PlatformTransactionManager(事务管理器)、TransactionDefinition(事务定义信息,隔离、传播、超时、只读)、TransactionStatus(事务具体运行状态)
1、事务管理器
相关的PlatformTransactionManager接口
2、事务隔离级别
当不考虑隔离性会引起一下问题:
(1)脏读:一个事务读取了另一个事务改写但未提交的数据,如果这些数据被回滚则读到的数据是无效的。A进行update、insert时候,B读取了中间状态不知道对不对的值。
(2)不可重复读:在同一事务中,多次读取同一数据返回的结果不同。B在A 的update操作前后,读到的数据不一致。
(3)幻读:一个事务读取了几行记录后,另一个事务插入一些记录,再后来查询中第一个事务就会发现有些原来没有的记录。B在A的insert操作前后,读到了的数据不一致。
事务的四种隔离级别,Mysql默认使用REPEATABLE_READ隔离级别,orcale默认使用READ_COMMOTTED
3、事务传播行为
事务传播行为:解决业务层方法之间的相互调用的问题
七种事务传播行为,其中分为三类。
第一类:PROPAGATION_REQUIRED(重点)、PROPAGATION_SUPPORTS、PROPAGATION_MANDATORY
第二类:PROPAGATION_REQUIRES_NEW(重点)、PROPAGATION_NOT_SUPPORTED、PROPAGATION_NEVER
第三类:PROPAGATION_NESTED(重点)
4、Spring事务管理的实现方式
(1)编码式事务管理:
手动编写代码进行事务管理(很少使用)
(2)声明式事务管理:
基于TransactionProxyFactoryBean的方式(很少使用):需要为每一个进行事务管理的类,配置一个TransactionProxyFactoryBean进行增强。
基于AspectJ的XML方式(经常使用):一旦配置好后,类不需要加任何东西。
基于注解的方式(经常使用):配置简单,需要在业务层类上或方法上加上一个注解@Transactional。
1、 手动编写代码进行事务管理
applicationContext.xml文件。(主要transactionManager和transactionTemplate)
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClass}" /> <property name="jdbcUrl" value="${jdbc.url}" /> <property name="user" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </bean> <bean id="accountDao" class="cn.muke.spring.demo1.AccountDaoImpl"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="accountService" class="cn.muke.spring.demo1.AccountServiceImpl"> <property name="accountDao" ref="accountDao"/> <property name="transactionTemplate" ref="transactionTemplate" /> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="transactionManager"/> </bean>
业务类
实现transactionTemplate的execute
import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; public class AccountServiceImpl implements AccountService { private AccountDao accountDao; private TransactionTemplate transactionTemplate; @Override public void transfer(final String out, final String in, Double money) { transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { accountDao.outMoney(out, money); int i = 1/0; accountDao.inMoney(in, money); } }); } public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } public void setTransactionTemplate(TransactionTemplate transactionTemplate) { this.transactionTemplate = transactionTemplate; } }
2、基于TransactionProxyFactoryBean的方式
applicationContext.xml(需要为每个类配置TransactionProxyFactoryBean)
<!-- 数据库连接 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClass}" /> <property name="jdbcUrl" value="${jdbc.url}" /> <property name="user" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </bean> <!-- 配置Dao层 --> <bean id="accountDao" class="cn.muke.spring.demo2.AccountDaoImpl"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 配置业务层 --> <bean id="accountService" class="cn.muke.spring.demo2.AccountServiceImpl"> <property name="accountDao" ref="accountDao"/> </bean> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 配置业务层的代理 --> <bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <!-- 配置目标代理对象 --> <property name="target" ref="accountService"/> <!-- 注入事务管理器 --> <property name="transactionManager" ref="transactionManager"/> <!-- 注入事务属性 --> <property name="transactionAttributes"> <props> <!-- prop的格式 * PROPAGATION :事务的传播行为 * ISOLATION :事务的隔离级别 * readOnly :只读 * -Exception :发生哪些异常回滚事务 * +Exception :发生哪些异常事务不回滚 --> <prop key="transfer">PROPAGATION_REQUIRED,+java.lang.ArithmeticException</prop> </props> </property> </bean>
业务层(不需做任何代码改动)
public class AccountServiceImpl implements AccountService { private AccountDao accountDao; @Override public void transfer(String out, String in, Double money) { accountDao.outMoney(out, money); int i = 1 / 0; accountDao.inMoney(in, money); } public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } }
3、基于AspectJ的XML方式
applicationContext.xml(txAdvice,切面编程)
<!-- 数据库连接 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClass}" /> <property name="jdbcUrl" value="${jdbc.url}" /> <property name="user" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </bean> <!-- 配置Dao层 --> <bean id="accountDao" class="cn.muke.spring.demo3.AccountDaoImpl"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 配置业务层 --> <bean id="accountService" class="cn.muke.spring.demo3.AccountServiceImpl"> <property name="accountDao" ref="accountDao"/> </bean> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置事务的通知:(事务的增强) --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!-- * propagation : 事务传播行为 * isolation : 事务隔离级别 * read-only : 只读 * rollback-for : 发生哪些异常回滚 * no-rollback-for : 发生哪些异常不回滚 * timeout : 事务超时时间 --> <tx:method name="transfer" propagation="REQUIRED" no-rollback-for="java.lang.ArithmeticException" /> </tx:attributes> </tx:advice> <!-- 配置切面 --> <aop:config> <!-- 配置切入点 --> <aop:pointcut expression="execution(* cn.muke.spring.demo3.AccountService+.*(..))" id="pointcut1"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/> </aop:config>
业务层(不需做任何代码改动)
public class AccountServiceImpl implements AccountService { private AccountDao accountDao; @Override public void transfer(String out, String in, Double money) { accountDao.outMoney(out, money); int i = 1 / 0; accountDao.inMoney(in, money); } public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } }
4、基于注解的方式
applicationContext.xml(开启注解事务)
<!-- 数据库连接 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClass}" /> <property name="jdbcUrl" value="${jdbc.url}" /> <property name="user" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </bean> <!-- 配置Dao层 --> <bean id="accountDao" class="cn.muke.spring.demo4.AccountDaoImpl"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 配置业务层 --> <bean id="accountService" class="cn.muke.spring.demo4.AccountServiceImpl"> <property name="accountDao" ref="accountDao"/> </bean> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 开启注解事务 --> <tx:annotation-driven transaction-manager="transactionManager"/>
业务层(增加注解@Transactional)
public class AccountServiceImpl implements AccountService { private AccountDao accountDao; /** * propagation :事务的传播行为 * isolation :事务的隔离级别 * readOnly :只读 * noRollbackFor :发生哪些异常事务不回滚 * noRollbackForClassName :发生哪些异常事务不回滚 * rollbackFor :发生哪些异常回滚事务 * rollbackForClassName :发生哪些异常回滚事务 */ @Override @Transactional(propagation = Propagation.REQUIRED) public void transfer(String out, String in, Double money) { accountDao.outMoney(out, money); try { int i = 1 / 0; } catch (Exception e) { throw new RuntimeException(); } accountDao.inMoney(in, money); } public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } }
项目代码在文件中