Spring事务管理
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring事务管理相关的知识,希望对你有一定的参考价值。
一、什么是事务
事务:指逻辑上的一组操作,这组操作要么全部成功,要么全部失败。有以下特性:
①原子性:事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
②一致性:事务前后数据的完整性必须保持一致(张三给李四转账,张三+李四的总额不变)。
③隔离性:多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务干扰,多个并发事务之间的数据相互隔离。
④持久性:一个事务一旦被提交,它对数据库中数据的改变就是永久性的,即使数据库发生故障也不该对其有任何影响。
二、事务管理接口
Spring的事务管理接口主要有三个:
①TransactionDefinition,事务定义信息(隔离,传播,超时,只读)
在Spring中,事务是通过TransactionDefinition接口来定义的,该接口包含与事务属性相关的方法,TransactionDefinition定义了五个表示隔离级别的常量,代表传播行为的常量,在TransactionDefinition中以int值表示超时时间。
- 隔离:防止数据库脏读、虚读等行为
- 传播:解决业务层方法之间的调用,传递事务
- 情形一,支持当前事务,如果不存在就创建一个;
- 情形二,如果有事务存在,挂起当前事务,创建一个新事务;
- 情形三,如果当前事务存在,嵌套事务执行。
这里找到一篇关于TransactionDefinition的文章:http://blog.csdn.net/japanstudylang/article/details/52916529
②Platform TransactionManager,平台事务管理器
Spring为不同的持久化框架提供了不同的平台事务管理器的接口实现:
- 使用Spring JDBC或者IBatis进行持久化数据时:org.springframework.jdbc.datasource.DataSourceTransactionManager
- 使用Hibernate5.0版本进行持久化数据时:org.springframework.orm.hibernate5.HibernateTransactionManager
Platform TransactionManager.getInstance()方法返回一个Transaction Status对象,返回的Transaction Status对象可能代表一个新的或已经存在的事务(如果当前调用堆栈中有一个符合条件的事务)。
③TransactionStatus,事务具体运行状态
Transaction Status接口提供了一个简单的控制事务查询和执行的方法。
三、Spring事务管理的方式
Spring的事务管理有两种方式:
①编程式的事务管理:编程式即采用注解的方式,需要注意的是,使用注解的方式需要在Spring的配置文件中加入一句话:<context:annotation-config />,其作用是开启注解的方式。这种方式在实际应用开发中很少使用,通过TransactionTemlate手动管理事务。
②使用XML配置声明式事务:声明式就是在Spring的配置文件中进行相关配置,Spring的声明式事务是通过AOP实现的,实质就是在方法执行前后进行拦截,然后在方法执行前创建并加入事务,执行完目标方法后根据执行情况提交事务或者回滚事务。开发中推荐使用(代码侵入性最小)。
Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource、TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分。 DataSource、 TransactionManager这两部分只是会根据数据访问方式有所变化,比如使用Hibernate进行数据访问时,DataSource实际为 SessionFactory,TransactionManager的实现为HibernateTransactionManager。
四、转账案例
①编程式的事务控制
- 在AccountService中使用TransactionTemplate
- TransactionTemplate依赖HibernateTransactionManager
- HibernateTransactionManager依赖LocalSessionFactoryBean
<!-- 组件扫描 --> <context:component-scan base-package="com.entor.transfer"></context:component-scan> <!-- 引入jdbc配置文件 --> <context:property-placeholder location="classpath:jdbc.properties" /> <!-- 配置c3p0连接池 --> <bean id="dataSource" destroy-method="close" 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> <!-- 配置hibernate SessionFactory --> <bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <!-- 指定hibernate的配置文件位置 --> <property name="configLocation" value="classpath:hibernate.cfg.xml"></property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop> <prop key="hibernate.hbm2ddl.auto">update</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hiberante.format_sql">true</prop> </props> </property> </bean> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <!-- 配置事务管理模版 --> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="transactionManager" /> </bean>
@Repository("accountDAO") /* 转账案例的DAO层接口实现类 */ public class AccountDAOImpl implements AccountDAO { @Autowired private SessionFactory sessionFactory; public SessionFactory getSessionFactory() { return sessionFactory; } /** * * @param out * 转出帐号 * @param money * 转出金额 */ @Override public void outMoney(String out, double money) { Session session = sessionFactory.getCurrentSession(); String hql = "from Account where name = :name"; Query query = session.createQuery(hql); query.setString("name", out); Account account = (Account) query.uniqueResult(); account.setMoney(account.getMoney() - money); session.save(account); } /** * * @param in * 转入帐号 * @param money * 转入金额 */ @Override public void inMoney(String in, double money) { Session session = sessionFactory.getCurrentSession(); String hql = "from Account where name = :name"; Query query = session.createQuery(hql); query.setString("name", in); Account account = (Account) query.uniqueResult(); account.setMoney(account.getMoney() + money); session.save(account); } }
@Service("accountService") public class AccountServiceImpl implements AccountService { @Autowired private AccountDAO accountDAO; // 注入事务模板 @Autowired private TransactionTemplate transactionTemplate; /** * * @param out * 转出帐号 * @param in * 转入帐号 * @param money * 转账金额 */ @Override public void transfer(final String out, final String in, final double money) { // 模版回调处理事务 this.transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult( TransactionStatus transactionStatus) { try { accountDAO.outMoney(out, money); // int i = 1 / 0;// 异常会导致入账失败 accountDAO.inMoney(in, money); } catch (Exception e) { System.out.println(e.getMessage()); transactionStatus.setRollbackOnly();// 事务回滚 } } }); } }
②声明式的事务控制
- 第一种方式:基于TransactionProxyFactoryBean
<!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </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 : 事务的传播行为 * ISOLATIONI : 事务隔离级别 * readOnly : 只读 * -Exception : 发生某些异常回滚 * +Exception : 发生某些异常不回滚 --> <!-- 保证不同的数据库操作在同一个事务当中 --> <prop key="transfer">PROPAGATION_REQUIRED</prop> </props> </property> </bean>
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:spring-account2.xml") public class UnitTest { /** * 注入代理类 */ @Resource(name = "accountServiceProxy") private AccountService accountService; /** * 注入工厂 */ @Resource private SessionFactory sessionFactory; @Test public void testAccount() { accountService.transfer("张三", "李四", 200); } }
- 第二种方式:基于AspectJ的XML配置方式(经常使用,配置清晰)
<!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <!-- 配置事务的通知 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!-- tx:method属性: * propagation : 事务的传播行为 * isolationi : 事务隔离级别 * readonly : 只读 * rollback-for : 发生某些异常回滚 * no-rollback-for : 发生某些异常不回滚 * timeout : 过期信息 --> <tx:method name="transfer" propagation="REQUIRED" timeout="-1"/> </tx:attributes> </tx:advice> <!-- 配置切面 --> <aop:config> <!-- 配置切入点 --> <aop:pointcut expression="execution(* com.entor.transfer3.service.impl.AccountServiceImpl.*(..))" id="pointcut" /> <!-- 配置切面 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut" /> </aop:config>
- 第三种方式:基于注解方式(经常使用,配置简单)
<!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <!-- 开启注解事务 --> <tx:annotation-driven transaction-manager="transactionManager" />
@Service("accountService") @Transactional // (propagation="",isolation="",readOnly="",noRollbackFor="",rollbackFor="",timeout="") public class AccountServiceImpl implements AccountService { @Autowired private AccountDAO accountDAO; /** * * @param out * 转出帐号 * @param in * 转入帐号 * @param money * 转账金额 */ @Override public void transfer(String out, String in, double money) { accountDAO.outMoney(out, money); int i = 1 / 0;// 异常会导致入账失败 accountDAO.inMoney(in, money); } }
注:找到一篇关于Spring事务管理的写得挺好的文章:https://www.cnblogs.com/newsouls/p/3988216.html
以上是关于Spring事务管理的主要内容,如果未能解决你的问题,请参考以下文章
初识Spring源码 -- doResolveDependency | findAutowireCandidates | @Order@Priority调用排序 | @Autowired注入(代码片段
Spring boot:thymeleaf 没有正确渲染片段
What's the difference between @Component, @Repository & @Service annotations in Spring?(代码片段
spring练习,在Eclipse搭建的Spring开发环境中,使用set注入方式,实现对象的依赖关系,通过ClassPathXmlApplicationContext实体类获取Bean对象(代码片段