spring事务管理
建maven项目,建立数据库表和初始化数据:
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');
1,pom.xml,引入需要的jar包(spring-test和junit版本要兼容,不然会报多种错误)
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.aluo</groupId> <artifactId>HelloSpring</artifactId> <version>0.0.1-SNAPSHOT</version> <build /> <dependencies> <!-- Spring Core --> <!-- http://mvnrepository.com/artifact/org.springframework/spring-core --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.1.4.RELEASE</version> </dependency> <!-- Spring Context --> <!-- http://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.1.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.1.4.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.15</version> </dependency> <!-- https://mvnrepository.com/artifact/c3p0/c3p0 --> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1</version> </dependency> <!-- https://mvnrepository.com/artifact/commons-dbcp/commons-dbcp --> <!-- <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.2.2</version> </dependency> --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <!-- 表示开发的时候引入,发布的时候不会加载此包 --> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>4.0.2.RELEASE</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>4.1.4.Release</version> </dependency> <dependency> <groupId> org.aspectj</groupId> <artifactId> aspectjweaver</artifactId> <version> 1.8.7</version> </dependency> </dependencies> </project>
2,新建接口AccountService
package com.aluo.spring.transaction; public interface AccountService { /** * * @param out * :转出账号 * @param in * :转入账号 * @param moneya * :转账金额 */ public void transfer(String out, String in, Double money); }
实现类AccountServiceImpl:
package com.aluo.spring.transaction; public class AccountServiceImpl implements AccountService { // 注入DAO类 private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } public void transfer(String out, String in, Double money) { accountDao.outMoney(out, money); accountDao.inMoney(in, money); } }
3,新建接口AccountDao:
package com.aluo.spring.transaction; 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); }
实现类AccountDaoImpl:
package com.aluo.spring.transaction; import org.springframework.jdbc.core.support.JdbcDaoSupport; public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { public void outMoney(String out, Double money) { String sql = "update account set money = money - ? where name = ?"; this.getJdbcTemplate().update(sql,money,out); } public void inMoney(String in, Double money) { String sql = "update account set money = money + ? where name = ?"; this.getJdbcTemplate().update(sql,money,in); } }
4,beans.xml:
<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" 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/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.driverClass}" /> <property name="jdbcUrl" value="${jdbc.url}" /> <property name="user" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </bean> <!-- 配置业务层 --> <bean id="accountService" class="com.aluo.spring.transaction.AccountServiceImpl"> <property name="accountDao" ref="accountDao" /> </bean> <!-- 配置DAO层,注入连接池就可以得到jdbc模板 --> <bean id="accountDao" class="com.aluo.spring.transaction.AccountDaoImpl"> <property name="dataSource" ref="dataSource" /> </bean> </beans>
5,jdbc.properties:
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/luo
jdbc.username=root
jdbc.password=123456
6,测试类SpringTransactionTest:
package com.aluo.spring.transaction; 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:beans3.xml") public class SpringTransactionTest { @Resource(name = "accountService") private AccountService accountService; @Test public void demo1() { System.out.println("begin"); accountService.transfer("aaa", "bbb", 300d); System.out.println("over"); } }
运行后:
假如出错,在AccountServiceImpl添加出错代码:
public void transfer(String out, String in, Double money) { accountDao.outMoney(out, money); int a = 12/0; accountDao.inMoney(in, money); }
再运行,如图:
aaa账户余额减少,但是bbb账号余额并没有增加。
所以,这个时候需要事务管理。有两种:1,编程式管理;2,声明式管理。
二,声明式管理
第一种:基于AspectJ的XML方式
bean.xml加入以下:
<!-- 配置事务管理器 --> <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="transfer" propagation="REQUIRED" /> </tx:attributes> </tx:advice> <!-- 配置切面 --> <aop:config> <!-- 配置切点 --> <aop:pointcut expression="execution(* com.aluo.spring.transaction.AccountService+.*(..))" id="pointcut1" /> <!-- 配置切面 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1" /> </aop:config>
pom.xml引入相关jar包。
此时,再运行SpringTransactionTest,结果如图:
和上图一样,说明事务起作用了。抛错之后,事务回滚了。
第二种:基于TransactionProxyFactoryBean的方式
配置事务管理器和业务层代理,在pom.xml加上:
<!-- 配置事务管理器 --> <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 key="transfer">PROPAGATION_REQUIRED</prop> </props> </property> </bean>
SpringTransactionTest改为如下:(引用代理类accountServiceProxy)
package com.aluo.spring.transaction; 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:beans3.xml") public class SpringTransactionTest { @Resource(name = "accountServiceProxy") private AccountService accountService; @Test public void demo1() { System.out.println("begin"); accountService.transfer("aaa", "bbb", 300d); System.out.println("over"); } }
运行,出错,事务有回滚。
第三种,基于注解
开启注解事务:
<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" 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/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.driverClass}" /> <property name="jdbcUrl" value="${jdbc.url}" /> <property name="user" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </bean> <!-- 配置业务层 --> <bean id="accountService" class="com.aluo.spring.transaction.AccountServiceImpl"> <property name="accountDao" ref="accountDao" /> </bean> <!-- 配置DAO层,注入连接池就可以得到jdbc模板 --> <bean id="accountDao" class="com.aluo.spring.transaction.AccountDaoImpl"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 开启注解事务 --> <tx:annotation-driven transaction-manager="transactionManager"/> </beans>
业务层加上注解@Transactional:
package com.aluo.spring.transaction; import org.springframework.transaction.annotation.Transactional; @Transactional public class AccountServiceImpl implements AccountService { // 注入DAO类 private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } public void transfer(String out, String in, Double money) { accountDao.outMoney(out, money); int a = 12 / 0; accountDao.inMoney(in, money); } }
运行,出错,事务有回滚。
Spring 的事务管理,是 AOP 的应用,将事务作为切面织入到了 Service 层的业务方法中。
参考:https://www.cnblogs.com/zhaozihan/p/6219776.html