Spring 声明式事务管理--准备工作以及三种声明式事务管理方式

xiaoxiao2021-02-28  177

使用转账实例来进行事务管理操作

目录结构以及需要的jar包

数据库脚本:

创建数据表account CREATE TABLE `account` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(20) NOT NULL, `money` double DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; INSERT INTO `account` VALUES ('1', 'aaa', '1000'); INSERT INTO `account` VALUES ('2', 'bbb', '1000'); INSERT INTO `account` VALUES ('3', 'ccc', '1000');

package com.spring.demo1; public interface AccountDao { /** * * @param out:转出账户 * @param money:转出金额 */ public void outMoney(String out,Double money); /** * * @param in:转入账户 * @param money:转入金额 */ public void inMoney(String in ,Double money); } package com.spring.demo1; /** * 转账案例的业务层接口 * @author liu * */ public interface AccountService { /** * * @param out:转出账户 * @param in:转入账户 * @param money:转账金额 */ public void transfer(String out,String in,Double money); } 实现类:

package com.spring.demo1; import org.springframework.jdbc.core.support.JdbcDaoSupport;; public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { /** * * @param out:转出账户 * @param money:转出金额 */ public void outMoney(String out, Double money) { String sql=" update account set money = money - ? where name=? "; //继承JdbcDaoSupport类 this.getJdbcTemplate().update(sql,money,out); } /** * * @param in:转入账户 * @param money:转入金额 */ public void inMoney(String in, Double money) { // TODO Auto-generated method stub String sql=" update account set money = money + ? where name=? "; this.getJdbcTemplate().update(sql,money,in); } } package com.spring.demo1; /** * 转账业务层实现类 * @author liu * */ public class AccountServiceImpl implements AccountService { //注入转账的Dao类 private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } /** * * @param out:转出账户 * @param in:转入账户 * @param money:转账金额 */ public void transfer(String out, String in, Double money) { // TODO Auto-generated method stub accountDao.outMoney(out, money); accountDao.inMoney(in, money); } } jdbc.properties:

jdbc.username=root jdbc.password=123456 jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc\:mysql\://127.0.0.1\:3306/springxml文件:

<?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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 引入外部的属性文件 --> <context:property-placeholder location="classpath:jdbc.properties"/> <!-- 配置c3p0连接池 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driver}"/> <property name="user" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> <property name="jdbcUrl" value="${jdbc.url}"/> </bean> <!-- 配置业务层 实现类--> <bean id="accountService" class="com.spring.demo1.AccountServiceImpl"> <property name="accountDao" ref="accountDao"/> </bean> <!-- 配置Dao 实现类 --> <bean id="accountDao" class="com.spring.demo1.AccountDaoImpl"> <property name="dataSource" ref="dataSource"></property> </bean> </beans> 测试类:

package com.spring.demo1; import javax.annotation.Resource; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:spring-transaction.xml") public class demo1 { //测试业务层类 @Resource(name="accountService") private AccountService accountService; @Test public void test(){ accountService.transfer("aaa", "bbb", 200.00); } }

log4j.properties:

log4j.rootLogger=debug, stdout, R log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p - %m%n log4j.appender.R=org.apache.log4j.RollingFileAppender log4j.appender.R.File=firestorm.log log4j.appender.R.MaxFileSize=100KB log4j.appender.R.MaxBackupIndex=1 log4j.appender.R.layout=org.apache.log4j.PatternLayout log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n log4j.logger.com.codefutures=DEBUG 结果:

==================================================

准备工作完成

Spring的声明式事务管理的方式一:编程式事务管理

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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 引入外部的属性文件 --> <context:property-placeholder location="classpath:jdbc.properties"/> <!-- 配置c3p0连接池 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driver}"/> <property name="user" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> <property name="jdbcUrl" value="${jdbc.url}"/> </bean> <!-- 配置业务层 实现类--> <bean id="accountService" class="com.spring.demo1.AccountServiceImpl"> <property name="accountDao" ref="accountDao"/> <!-- 那个类要进行事务管理就在那个类中注入事务管理模版 --> <property name="transactionTemplate" ref="transactionTemplate"/> </bean> <!-- 配置Dao 实现类 --> <bean id="accountDao" class="com.spring.demo1.AccountDaoImpl"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置事务管理器 jdbc 注入dataSource 得到数据库资源--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--配置事务管理的模板:Spring为了简化事务管理的代码 而提供的类 --> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="transactionManager"></property> </bean> </beans> Service实现类:

package com.spring.demo1; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; /** * 转账业务层实现类 * @author liu * */ public class AccountServiceImpl implements AccountService { //注入转账的Dao类 private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } //注入事务管理模板: private TransactionTemplate transactionTemplate; public void setTransactionTemplate(TransactionTemplate transactionTemplate) { this.transactionTemplate = transactionTemplate; } /** * * @param out:转出账户 * @param in:转入账户 * @param money:转账金额 */ public void transfer(final String out, final String in, final Double money) { // TODO Auto-generated method stub // accountDao.outMoney(out, money); // // accountDao.inMoney(in, money); transactionTemplate.execute(new TransactionCallbackWithoutResult() { /** * 匿名内部类的形式 * 匿名内部类的形式无法访问外部的参数只需要把参数变为final 就行了 * 得到事务管理状态的对象:transactionStatus */ @Override protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { // TODO Auto-generated method stub accountDao.outMoney(out, money); // int i=1/0; accountDao.inMoney(in, money); } }); } } 结果:

Spring的声明式事务管理的方式二:基于AspectJ的xml方式的配置

相对于准备工作:不需要修改各个类和接口只需要修改配置文件:

spring-transaction3.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" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- 引入外部的属性文件 --> <context:property-placeholder location="classpath:jdbc.properties"/> <!-- 配置c3p0连接池 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driver}"/> <property name="user" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> <property name="jdbcUrl" value="${jdbc.url}"/> </bean> <!-- 配置业务层 实现类--> <bean id="accountService" class="com.spring.demo3.AccountServiceImpl"> <property name="accountDao" ref="accountDao"/> </bean> <!-- 配置Dao 实现类 --> <bean id="accountDao" class="com.spring.demo3.AccountDaoImpl"> <property name="dataSource" ref="dataSource"></property> </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:事务的隔离级别 * readOnly:只读(不可以进行修改,删除。插入操作的) *-Exception :发生哪些异常回滚事务 *+Exception : 发生哪些异常事务不会滚 --> <tx:method name="transfer" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!-- 配置切面 --> <aop:config> <!-- 配置切入点 第一个*为方法的返回值,任意的返回值 后面有空格;第二个*:该类的子类的所有方法; +代表的是该类的子类都会有这么一个切点--> <aop:pointcut expression="execution(* com.spring.demo3.AccountService+.*(..))" id="pointcut1"/> <!-- 配置切面 advisor是一个切入点 一个通知 aspect是多个切入点多个通知--> <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/> </aop:config> </beans> Spring的声明式事务管理的方式三:基于基于注解的事务管理方式

也只需要改配置文件   只需要在需要事务管理的类级别上加上注解@Transactional 就可以了 

spring-transaction4.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" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- 引入外部的属性文件 --> <context:property-placeholder location="classpath:jdbc.properties"/> <!-- 配置c3p0连接池 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driver}"/> <property name="user" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> <property name="jdbcUrl" value="${jdbc.url}"/> </bean> <!-- 配置业务层 实现类--> <bean id="accountService" class="com.spring.demo4.AccountServiceImpl"> <property name="accountDao" ref="accountDao"/> </bean> <!-- 配置Dao 实现类 --> <bean id="accountDao" class="com.spring.demo4.AccountDaoImpl"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 开启基于注解事务 --> <tx:annotation-driven transaction-manager="transactionManager"/> </beans> AccountServiceImpl: package com.spring.demo4; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; /** * 转账业务层实现类 * @author liu *@Transactional注解中的属性 * * @Transactional注解的属性 * propagation :事务的传播行为 * isolation :事务的隔离级别 * readOnly :只读 * rollbackFor :发生哪些异常回滚 * norollbackFor:发生哪些异常不回滚 * *@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.DEFAULT,readOnly=false) */ @Transactional() public class AccountServiceImpl implements AccountService { //注入转账的Dao类 private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } /** * * @param out:转出账户 * @param in:转入账户 * @param money:转账金额 */ public void transfer(String out, String in,Double money) { // TODO Auto-generated method stub accountDao.outMoney(out, money); // int i=1/0; accountDao.inMoney(in, money); } } 总结:

常用的方式是后两种,减少与service层代码的耦合,无论使用三种中的哪一种,都需要在xml文件中注入

<!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> Spring事务管理的常用接口: 首先会根据TransactionDefinition事务定义的信息(比如定义了什么样的隔离级别,定义什么样的传播行为),由PlatformTransactionManager对事务进行管理,进行事务管理的过程中,事务会产生一些相应的状态,这些状态在TransactionStatus中。 ----------------------------------------------------- Spring为不同的持久化框架提供了不通的PlatformTransactionManager接口的实现 什么情况,用什么实现类   常用的接口实现 1. 实现类: org.springframework.jdbc.datasource.DataSourceTransactionManager 适用情况:使用Spring JDBC 或iBatis进行持久化数据时使用 2.  实现类:org.springframework.orm.hibernate3.HibernateTrasactionManager 适用情况:使用Hibernate3.0版本进行持久化数据时使用 3. .................... --------------------------------------------- TransactionDefinition接口,(事务定义的信息) ①.isolation    :事务的隔离级别:用来解决脏读,不可重复读,虚读(幻读) isolation=Isolation.DEFAULT-->使用后端数据库默认的隔离级别 mysql数据库的默认隔离级别是:READ_COMMITTED(可以防止脏读,不可重复读,但幻读仍可能发生) .................... ②.propagation  :事务的传播行为:主要是解决业务层方法之间的相互调用的问题(理解还需提高) 事务的传播行为有七种,又分为三类: 第一类共同点:如果 A 方法中有事务,则调用 B 方法时就用该事务,即:A和B方法在同一个事务中。 PROPAGATION_REQUIRED:如果 A 方法中没有事务,则调用 B 方法时就创建一个新的事务。 PROPAGATION_SUPPORTS:如果 A 方法中没有事务,则调用 B 方法时就不使用该事务。 PROPAGATION_MANDATORY:如果 A 方法中没有事务,则调用 B 方法时就抛出异常。 第二类共同点:A方法和B方法没有在同一个事务里面。 PROPAGATION_REQUIRES_NEW:如果 A 方法中有事务,则挂起并新建一个事务给 B 方法。 PROPAGATION_NOT_SUPPORTED:如果 A 方法中有事务,则挂起。 PROPAGATION_NEVER:如果 A 方法中有事务,则报异常。 PROPAGATION_REQUIRES_NEW与PROPAGATION_REQUIRED 的事务区别在于事务的回滚程度了 第三类:如果 A 方法有的事务执行完,设置一个保存点,如果 B 方法中事务执行失败,可以滚回保存点或初始状态。

PROPAGATION_NESTED :如果当前事务存在,则嵌套事务执行。

理解Nested的关键是savepoint。他与PROPAGATION_REQUIRES_NEW的区别是,PROPAGATION_REQUIRES_NEW另起一个事务,将会与他的父事务相互独立,而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,如果父事务最后回滚,他也要回滚的。  而Nested事务的好处是他有一个savepoint。 

参考博客:浅析Spring事务传播行为

TransactionStatus接口用来记录事务的状态 平台事务管理器(PlatformTransactionManager)会根据TransactionDefinition中定义的事务信息(包括隔离级别、传播行为)来进行事务的管理,在管理的过程中事务可能产生了保存点或事务是新的事务等情况,那么这些信息都会记录在TransactionStatus的对象中.

转载请注明原文地址: https://www.6miu.com/read-19925.html

最新回复(0)