Spring的事务管理
Posted X凯
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring的事务管理相关的知识,希望对你有一定的参考价值。
什么是事务?
l 事务:逻辑上的一组操作,组成这组操作的各个单元,要么全都成功,要么全都失败。
理解事务之前,先讲一个你日常生活中最常干的事:转账。
比如你给朋友转账1000块钱,大体有两个步骤:首先输入密码金额,你的银行卡扣掉1000元钱;然后你朋友账上增加1000元钱。这两个步骤必须是要么都执行要么都不执行。如果银行卡扣除了1000块但是转账失败,你朋友没收到1000元的话,你将会损失1000元;如果银行卡扣钱失败但是你朋友账户却增加了1000块,那么银行将损失1000元。所以,如果一个步骤成功另一个步骤失败对双方都不是好事,如果不管哪一个步骤失败了以后,整个转账过程都能回滚,也就是完全取消所有操作的话,这对双方都是极好的。
事务就是用来解决类似问题的。事务是一系列的动作,它们综合在一起才是一个完整的工作单元,这些动作必须全部完成,如果有一个失败的话,那么事务就会回滚到最开始的状态,仿佛什么都没发生过一样。
在企业级应用程序开发中,事务管理必不可少的技术,用来确保数据的完整性和一致性。
事务有四个特性:ACID
l 原子性(Atomicity):事务不可分割
l 一致性(Consistency):事务执行前后数据完整性保持一致
l 隔离性(Isolation):一个事务的执行不应该受到其他事务的干扰
l 持久性(Durability):一旦事务结束,数据就持久化到数据库
Spring的事务管理的API
PlatformTransactionManager:平台事务管理器
•平台事务管理器:接口,是Spring用于管理事务的真正的对象。
♦DataSourceTransactionManager :底层使用JDBC管理事务
♦ HibernateTransactionManager :底层使用Hibernate管理事务
TransactionDefinition :事务定义信息
•事务定义:用于定义事务的相关的信息,隔离级别、超时信息、传播行为、是否只读
TransactionStatus:事务的状态
•事务状态:用于记录在事务管理过程中,事务的状态的对象。
事务管理的API的关系:
Spring进行事务管理的时候,首先平台事务管理器根据事务定义信息进行事务的管理,在事务管理过程中,
产生各种状态,将这些状态的信息记录到事务状态的对象中。
Spring的事务的传播行为
* 事务的传播行为主要用来解决业务层方法相互调用的问题。
l Spring中提供了七种事务的传播行为(可分为三类):
♦ 保证多个操作在同一个事务中
•
PROPAGATION_REQUIRED
:默认值,如果A方法有事务,使用A中的事务,如果A没有,创建一个新的事务,将操作包含进来
•
PROPAGATION_SUPPORTS
:支持事务,如果A方法有事务,使用A中的事务。如果A没有事务,不使用事务。
• PROPAGATION_MANDATORY
:如果A方法有事务,使用A中的事务。如果A没有事务,抛出异常。
♦ 保证多个操作不在同一个事务中
•
PROPAGATION_REQUIRES_NEW
:如果A有事务,将A的事务挂起(暂停),创建新事务,只包含自身操作。如果A中没有事务,创建一个新事务,包含自身操作。
•
PROPAGATION_NOT_SUPPORTED
:如果A有事务,将A的事务挂起。不使用事务管理。
•
PROPAGATION_NEVER
:如果A有事务,报异常。
♦ 嵌套式事务
•
PROPAGATION_NESTED
:嵌套事务,如果A有事务,按照A的事务执行,执行完成后,设置一个保存点,执行B的操作,如果没有异常,执行通过,如果有异常,可以选择回滚到最初始位置,也可以回滚到保存点。
接着模拟一下上述转账这个过程的事务管理
先搭建这个事物管理环境:
创建Service的接口和实现类
/** * 转账的业务层的实现类 * @author jt * */ public class AccountServiceImpl implements AccountService { // 注入DAO: private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } @Override /** * from:转出账号 * to:转入账号 * money:转账金额 */ public void transfer( String from, String to, Double money) { accountDao.outMoney(from, money); // int d = 1/0; // 上一行是模拟转账过程出问题,这里先暂时注释掉。 accountDao.inMoney(to, money); } }
创建DAO的接口和实现类
/** * 转账的DAO的实现类 * @author jt * */ public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { //这里继承了JdbcDaoSupport是模版类,就省的再进行模版注入了 @Override public void outMoney(String from, Double money) { this.getJdbcTemplate().update("update account set money = money - ? where name = ?", money,from); } @Override public void inMoney(String to, Double money) { this.getJdbcTemplate().update("update account set money = money + ? where name = ?", money ,to); } }
配置Service和DAO:交给Spring管理
<!-- 配置Service============= --> <bean id="accountService" class="com.xk.xx.xx.AccountServiceImpl"> <property name="accountDao" ref="accountDao"/> </bean> <!-- 配置DAO================= --> <bean id="accountDao" class="com.xk.xx.xx.AccountDaoImpl"> <property name="dataSource" ref="dataSource"/> </bean>
配置连接池和JDBC的模板
<!-- 配置连接池 --> <!-- 第二种方式通过context标签引入的 --> <context:property-placeholder location="classpath:jdbc.properties"/> <!-- 配置C3P0连接池=============================== --> <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>
这是运行测试类之前的数据库数据。
编写测试类:
/** * 测试转账的环境 * @author jt * */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class SpringDemo1 { @Resource(name="accountService") private AccountService accountService; @Test public void demo1(){ accountService.transfer("甲", "丁", 1000d); } }
运行测试类后数据库数据如下:
转账正常进行。
接着将数据恢复转账之前,然后将之前AccountServiceImpl中提到的注释打开,模拟转账出现问题。然后运行测试类
会发现,甲的钱被扣了1000,但是丁却没有收到
这就是开头所举例子。
那么接下来用事物管理来解决这种问题的发生。
首先配置事务管理器
<!-- 配置事务管理器=============================== --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
配置增强
<!-- 配置事务的增强=============================== --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!-- 事务管理的规则 --> <!-- <tx:method name="save*" propagation="REQUIRED" isolation="DEFAULT"/> <tx:method name="update*" propagation="REQUIRED"/> <tx:method name="delete*" propagation="REQUIRED"/> <tx:method name="find*" read-only="true"/> --> <tx:method name="*" propagation="REQUIRED" read-only="false"/> </tx:attributes> </tx:advice>
AOP的配置
<!-- aop的配置 --> <aop:config> <aop:pointcut expression="execution(* com.xk.xx.xx.AccountServiceImpl.*(..))" id="pointcut1"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/> </aop:config>
再次运行测试类:
发现转账失败,但是没有扣甲的钱,丁的钱也没有增加
因为转账一旦出现问题,整个过程都会回滚。
这就是事务管理的作用之一。
上面采用的声明式事务管理是用xml配的。采用的是AOP思想。
也可以选择用注解的方式:
这里,配置文件中,只需配置事务管理器然后开启注解事务即可
<!-- 配置事务管理器=============================== --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 开启注解事务=============== --> <tx:annotation-driven transaction-manager="transactionManager"/>
然后在业务层添加注解
@Transactional还有许多属性,根据需要添加即可
注解的方式,配置少些,但是每次必须自行给需要的业务层添加注解。
以上是关于Spring的事务管理的主要内容,如果未能解决你的问题,请参考以下文章
初识Spring源码 -- doResolveDependency | findAutowireCandidates | @Order@Priority调用排序 | @Autowired注入(代码片段
初识Spring源码 -- doResolveDependency | findAutowireCandidates | @Order@Priority调用排序 | @Autowired注入(代码片段
spring练习,在Eclipse搭建的Spring开发环境中,使用set注入方式,实现对象的依赖关系,通过ClassPathXmlApplicationContext实体类获取Bean对象(代码片段
Spring Rest 文档。片段生成时 UTF-8 中间字节无效 [重复]
What's the difference between @Component, @Repository & @Service annotations in Spring?(代码片段