Spring中的事务管理
Spring中的事务管理
事务的概念
事务
是逻辑上一组操作,要么全都成功,要么全都失败。
事务特性-ACID
原子性:事务不可分割
一致性:事务执行的前后,数据完整性保持一致.
隔离性:一个事务执行的时候,不应该受到其他事务的打扰
持久性:一旦结束,数据就永久的保存到数据库.
脏读、不可重复读、虚读
如果不考虑隔离性
脏读:一个事务读到另一个事务未提交数据。
不可重复读:一个事务读到另一个事务已经提交数据(update)导致一个事务多次查询结果不一致
虚读:一个事务读到另一个事务已经提交数据(insert)导致一个事务多次查询结果不一致
事务的隔离级别
未提交读:以上情况都有可能发生。
已提交读:避免脏读,但不可重复读,虚读是有可能发生。
可重复读:避免脏读,不可重复读,但是虚读有可能发生。
串行的:避免以上所有情况。
事务管理之事务开发常用API的详解
分层开发:事务处在Service层。
Spring事务管理高层抽象主要包括3个接口,它们的常见方法:
(1)PlatformTransactionManager:平台事务管理器
Method Summary | |
---|---|
void |
commit(TransactionStatus status) 提交 |
TransactionStatus |
getTransaction(TransactionDefinition definition) 获取事务 |
void |
rollback(TransactionStatus status) 回滚 |
(2)TransactionDefinition:事务定义
ISOLation_XXX:设置事务的隔离级别
PROPAGATION_XXX:设置事务的传播行为。不是JDBC中有的,是为了解决实际开发中的问题,后面会详细的说。
TIMEOUT_DEFAULT:过期时间
Field Summary | |
---|---|
static int |
ISOLATION_DEFAULT Use the default isolation level of the underlying datastore. |
static int |
ISOLATION_READ_COMMITTED Indicates that dirty reads are prevented; non-repeatable reads and phantom reads can occur. |
static int |
ISOLATION_READ_UNCOMMITTED Indicates that dirty reads, non-repeatable reads and phantom reads can occur. |
static int |
ISOLATION_REPEATABLE_READ Indicates that dirty reads and non-repeatable reads are prevented; phantom reads can occur. |
static int |
ISOLATION_SERIALIZABLE Indicates that dirty reads, non-repeatable reads and phantom reads are prevented. |
static int |
PROPAGATION_MANDATORY Support a current transaction; throw an exception if no current transaction exists. |
static int |
PROPAGATION_NESTED Execute within a nested transaction if a current transaction exists, behave like PROPAGATION_REQUIRED else. |
static int |
PROPAGATION_NEVER Do not support a current transaction; throw an exception if a current transaction exists. |
static int |
PROPAGATION_NOT_SUPPORTED Do not support a current transaction; rather always execute non-transactionally. |
static int |
PROPAGATION_REQUIRED Support a current transaction; create a new one if none exists. |
static int |
PROPAGATION_REQUIRES_NEW Create a new transaction, suspending the current transaction if one exists. |
static int |
PROPAGATION_SUPPORTS Support a current transaction; execute non-transactionally if none exists. |
static int |
TIMEOUT_DEFAULT Use the default timeout of the underlying transaction system, or none if timeouts are not supported. |
Method Summary | |
---|---|
int |
getIsolationLevel() Return the isolation level. |
String |
getName() Return the name of this transaction. |
int |
getPropagationBehavior() Return the propagation behavior. |
int |
getTimeout() Return the transaction timeout. |
boolean |
isReadOnly() Return whether to optimize as a read-only transaction. |
(3)TransactionStatus:事务状态
是否有保存点,是否一个新事务,是否已经提交,是否已经回滚
Method Summary | |
---|---|
void |
flush() Flush the underlying session to the datastore, if applicable: for example, all affected Hibernate/JPA sessions. |
boolean |
hasSavepoint() Return whether this transaction internally carries a savepoint, that is, has been created as nested transaction based on a savepoint. |
boolean |
isCompleted() Return whether this transaction is completed, that is, whether it has already been committed or rolled back. |
boolean |
isNewTransaction() Return whether the present transaction is new (else participating in an existing transaction, or potentially not running in an actual transaction in the first place). |
boolean |
isRollbackOnly() Return whether the transaction has been marked as rollback-only (either by the application or by the transaction infrastructure). |
void |
setRollbackOnly() Set the transaction rollback-only. |
这三个接口的关系:
PlatformTransactionManager通过TransactionDefinition设置事务相关信息管理事务,管理事务过程中,产生一些事务状态:状态由TransactionStatus记录.
API详解:
PlatformTransactionManager:接口.
Spring为不同的持久化框架提供了不同PlatformTransactionManager接口实现
org.springframework.jdbc.datasource.DataSourceTransactionManager : 使用SpringJDBC或iBatis进行持久化数据时使用
org.springframework.orm.hibernate3.HibernateTransactionManager : 使用Hibernate3.0版本进行持久化数据时使用
org.springframework.orm.jpa.JpaTransactionManager 使用JPA进行持久化时使用
org.springframework.jdo.JdoTransactionManager 当持久化机制是Jdo时使用
org.springframework.transaction.jta.JtaTransactionManager 使用一个JTA实现来管理事务,在一个事务跨越多个资源时必须使用
TransactionDefinition:接口
ISOLation_DEFAULT:默认级别 MySQL:repeatable_read;Oracle:read_commited
事务的传播行为:不是JDBC事务管理,解决业务层之间的调用的事务的关系
PROPAGATION_REQUIRED:支持当前事务,如果不存在就新建一个
* A,B 如果A有事务,B使用A的事务,如果A没有事务,B就开启一个新的事务.(A,B是在一个事务中。)
PROPAGATION_SUPPORTS :支持当前事务,如果不存在,就不使用事务
* A,B 如果A有事务,B使用A的事务,如果A没有事务,B就不使用事务.
PROPAGATION_MANDATORY :支持当前事务,如果不存在,抛出异常
* A,B 如果A有事务,B使用A的事务,如果A没有事务,抛出异常.
PROPAGATION_REQUIRES_NEW 如果有事务存在,挂起当前事务,创建一个新的事务
* A,B 如果A有事务,B将A的事务挂起,重新创建一个新的事务.(A,B不在一个事务中.事务互不影响.)
PROPAGATION_NOT_SUPPORTED 以非事务方式运行,如果有事务存在,挂起当前事务
* A,B 非事务的方式运行,A有事务,就会挂起当前的事务.
PROPAGATION_NEVER 以非事务方式运行,如果有事务存在,抛出异常
PROPAGATION_NESTED 如果当前事务存在,则嵌套事务执行
* 基于SavePoint技术
* A,B A有事务,A执行之后,将A事务执行之后的内容保存到SavePoint.B事务有异常的话,用户需要自己设置事务提交还是回滚
事务管理快速入门实例
Spring的事务管理分成两类:
1、 编程式事务管理:手动编写代码完成事务管理。在实际应用中很少使用,通过TransactionTemplate手动管理事务。
2、声明式事务管理:不需要手动编写代码,配置。使用XML配置声明式事务,开发中推荐使用(代码侵入性最小),Spring的声明式事务是通过AOP实现的。
以转账案例为基本场景,下面介绍转账案例的环境准备:
(1)创建数据库表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');
(2)创建一个web项目,导入相应的jar包(基本开发包、mysql驱动、C3P0连接池)
(3)src下建立配置文件applicationContext.xml、jdbc.properties、log4j.properties
jdbc.driver = com.mysql.jdbc.Driver jdbc.url = jdbc:mysql://127.0.0.1:3306/springjdbc?characterEncoding=utf-8 jdbc.name = root jdbc.password = 123456
(4)新建两个接口
package com.js.demo2; public interface AccountDao { /** * 转出的方法 * @param from:转出得人 * @param money:转账金额 */ public void out(String from,Double money); /** * 转入的方法 * @param to:转入的人 * @param money:转账金额 */ public void in(String to,double money); }
package com.js.demo2; /** * 账户的业务层的接口 * @author JiangShuai * */ public interface AccountService { /** * 转账的方法 * @param from:从哪转出 * @param to:转入目标 * @param money:转账金额 */ public void transfer(String from,String to,double money); }
(6)编写两个实现类
package com.js.demo2; import org.springframework.jdbc.core.support.JdbcDaoSupport; public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { /** * 转出的方法 * @param from:转出得人 * @param money:转账金额 */ public void out(String from, Double money) { String sql = "update account set money = money - ? where name = ?"; this.getJdbcTemplate().update(sql,money,from); } /** * 转入的方法 * @param to:转入的人 * @param money:转账金额 */ public void in(String to, double money) { String sql = "update account set money = money + ? where name = ?"; this.getJdbcTemplate().update(sql,money,to); } }
package com.js.demo2; public class AccountServiceImpl implements AccountService{ private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } /** * 转账的方法 * @param from:从哪转出 * @param to:转入目标 * @param money:转账金额 */ public void transfer(String from, String to, double money) { accountDao.out(from, money); // int i = 1/0; //制造异常 accountDao.in(to, money); System.out.println("用户"+from+"向用户"+to+"转账"+money+"元"); } }
(7)在配置文件中配置bean
<?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:util="http://www.springframework.org/schema/util" 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/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 第1种,配置属性文件:配置bean,暂时注释 --> <!-- <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location" value="classpath:jdbc.properties"></property> </bean> --> <!-- 第2种,配置属性文件:引入context约束,然后解析属性文件 --> <context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置C3P0连接池 -->
<bean id="C3P0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.name}"></property>
<property name="password" value="${jdbc.password}">
</property> </bean>
<!-- 定义JDBC的模板类 -->
<bean id="C3P0JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="C3P0DataSource"></property> </bean>
<!-- 业务层类 --> <bean id="accountService" class="com.js.demo2.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property> </bean>
<!-- 持久层类 --> <bean id="accountDao" class="com.js.demo2.AccountDaoImpl">
<!-- 注入连接池对象,通过连接池对象去创建JDBC模板 -->
<property name="dataSource" ref="C3P0DataSource"></property>
</bean>
</beans>
(8)测试一下这个场景的功能
package com.js.demo2; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * 测试转账功能 * @author hdb * */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class C3P0DataSourceAccountTest { @Autowired @Qualifier("accountService") private AccountService accountService; @Test public void demo(){ //完成转账 accountService.transfer("aaa", "bbb", 100d); } }
查看数据库:
金额改变正确,说明功能正常。
这时候,系统内不存在事务,如果在转账过程中有异常发生,取消
AccountServiceImpl 中注释//int i = 1/0; //制造异常
重新运行测试:
发现“aaa”的钱扣了,但是“bbb”的钱没增加。
显然,这时候就需要有事务的管理了。
Spring提供了事务管理的模板,也就是一个工具类,可以省写一些代码。
第一步,注册事务管理器、事务管理模板,并在业务层注入事务管理模板,基于applicationContext.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:util="http://www.springframework.org/schema/util" 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/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 第1种,配置属性文件:配置bean,暂时注释 --> <!-- <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location" value="classpath:jdbc.properties"></property> </bean> --> <!-- 第2种,配置属性文件:引入context约束,然后解析属性文件 --> <context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置C3P0连接池 --> <bean id="C3P0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driver}"></property> <property name="jdbcUrl" value="${jdbc.url}"></property> <property name="user" value="${jdbc.name}"></property> <property name="password" value="${jdbc.password}"></property> </bean> <!-- 定义JDBC的模板类 --> <bean id="C3P0JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="C3P0DataSource"></property> </bean> <!-- 业务层类 --> <bean id="accountService" class="com.js.demo2.AccountServiceImpl"> <!-- 在业务层注入Dao --> <property name="accountDao" ref="accountDao"></property> <!-- 在业务层注入事务管理模板 --> <property name="transactionTemplate" ref="transactionTemplate"></property> </bean> <!-- 持久层类 --> <bean id="accountDao" class="com.js.demo2.AccountDaoImpl"> <!-- 注入连接池对象,通过连接池对象去创建JDBC模板 --> <property name="dataSource" ref="C3P0DataSource"></property> </bean> <!-- 事务管理的模板 --> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="transactionManager"></property> </bean> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 需要注入连接池,通过连接池获得连接 --> <property name="dataSource" ref="C3P0DataSource"></property> </bean> </beans>
第二步:修改业务类,添加事务模板属性、编写事务。
package com.js.demo2; 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; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } public void setTransactionTemplate(TransactionTemplate transactionTemplate) { this.transactionTemplate = transactionTemplate; } /** * 转账的方法 * @param from:从哪转出 * @param to:转入目标 * @param money:转账金额 */ public void transfer(final String from,final String to,final double money) { //编写事务 transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { try{ accountDao.out(from, money); int i = 1/0; //制造异常 accountDao.in(to, money); System.out.println("用户"+from+"向用户"+to+"转账"+money+"元"); }catch (Exception e) { e.printStackTrace(); } } }); } }
第三步:进行测试,和上面的一样。
第四步:测试有异常发生的情况下,事务的管理是否生效。
如果在转账过程中有异常发生,取消
AccountServiceImpl 中注释//int i = 1/0; //制造异常
第五步:重新运行测试。
抛出异常,金额没有发生变化,说明事务管理成功。
这就是使用手动编码的方式来实现事务的管理。
手动编码方式的缺点:代码量增加,代码有侵入性。业务层代码中嵌入了dao代码。
事务管理之声明式事务-基于TransactionProxyBean
声明式事务管理(原始方式,基于TransactionProxyBean,它可以生成一个基于事务的对象代理,在实际应用中很少使用)的的具体步骤:
另外,还需要引入aop包和aop联盟的包
1、配置applicationContext.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:util="http://www.springframework.org/schema/util" 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/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 第1种,配置属性文件:配置bean,暂时注释 --> <!-- <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location" value="classpath:jdbc.properties"></property> </bean> --> <!-- 第2种,配置属性文件:引入context约束,然后解析属性文件 --> <context:property-placeholder location="classpath:jdbc.properties"/> <!-- 配置C3P0连接池 --> <bean id="C3P0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driver}"></property> <property name="jdbcUrl" value="${jdbc.url}"></property> <property name="user" value="${jdbc.name}"></property> <property name="password" value="${jdbc.password}"></property> </bean> <!-- 定义JDBC的模板类 --> <bean id="C3P0JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="C3P0DataSource"></property> </bean> <!-- 业务层类 --> <bean id="accountService" class="com.js.demo2.AccountServiceImpl"> <!-- 在业务层注入Dao --> <property name="accountDao" ref="accountDao"></property> </bean> <!-- 持久层类 --> <bean id="accountDao" class="com.js.demo2.AccountDaoImpl"> <!-- 注入连接池对象,通过连接池对象去创建JDBC模板 --> <property name="dataSource" ref="C3P0DataSource"></property> </bean> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 需要注入连接池,通过连接池获得连接 --> <property name="dataSource" ref="C3P0DataSource"></property> </bean> <!-- 配置生成代理对象 --> <bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <!-- 目标对象 --> <property name="target" ref="accountService"></property> <!-- 注入事务管理器 --> <property name="transactionManager" ref="transactionManager"></property> <!-- 设置事务的一些属性 --> <property name="transactionAttributes"> <props> <prop key="transfer">PROPAGATION_REQUIRED</prop> </props> </property> </bean> </beans>
【补充】:prop参数配置的格式:PROPAGATION,ISOLATION,readOnly,-Exception,+Exception
-顺序:传播行为、隔离级别、事务是否只读、发生哪些异常可以回滚事务(所有的异常都回滚)、发生了哪些异常不回滚
2、编写测试类
package com.js.demo2; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * 测试转账功能 * @author hdb * */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class C3P0DataSourceAccountTest { @Autowired @Qualifier("accountServiceProxy") private AccountService accountService; @Test public void demo(){ //完成转账 accountService.transfer("aaa", "bbb", 100d); } }
测试有异常发生的情况下,事务的管理是否生效,详细参考上面。
事务管理之声明式事务-基于切面自动代理
声明式事务的管理:(自动代理,基于切面),这种方式需要重点掌握。
第1步:导入相应的jar包。aspectJ包(两个)
第2步:引入相应的约束 aop约束、tx约束,配置事务管理器,配置增强、切面、切点
<?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:util="http://www.springframework.org/schema/util" 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/util http://www.springframework.org/schema/util/spring-util.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"> <!-- 纯用注解配置bean的情况下context:annotation-config可以省略,当混合使用xml和注解配置bean的时候就不能省略了--> <!-- 作用是使@Resource、@PostConstruct、@PreDestroy、@AutoWired生效--> <context:annotation-config></context:annotation-config> <!-- 第1种,配置属性文件:配置bean,暂时注释 --> <!-- <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location" value="classpath:jdbc.properties"></property> </bean> --> <!-- 第2种,配置属性文件:引入context约束,然后解析属性文件 --> <context:property-placeholder location="classpath:jdbc.properties"/> <!-- 配置C3P0连接池 --> <bean id="C3P0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driver}"></property> <property name="jdbcUrl" value="${jdbc.url}"></property> <property name="user" value="${jdbc.name}"></property> <property name="password" value="${jdbc.password}"></property> </bean> <!-- 定义JDBC的模板类 --> <bean id="C3P0JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="C3P0DataSource"></property> </bean> <!-- 业务层类 --> <bean id="accountService" class="com.js.demo2.AccountServiceImpl"> <!-- 在业务层注入Dao --> <property name="accountDao" ref="accountDao"></property> </bean> <!-- 持久层类 --> <bean id="accountDao" class="com.js.demo2.AccountDaoImpl"> <!-- 注入连接池对象,通过连接池对象去创建JDBC模板 --> <property name="dataSource" ref="C3P0DataSource"></property> </bean> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 需要注入连接池,通过连接池获得连接 --> <property name="dataSource" ref="C3P0DataSource"></property> </bean> <!-- 定义一个增强 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!-- 增强事务的属性的配置 --> <!-- isolation:事务的隔离级别 propagation:事务的传播行为 read-only:false,不是只读 timeout:-1不过期 no-rollback-for:发生哪些异常不回滚 rollback-for:发生哪些异常回滚 --> <tx:attributes> <tx:method name="transfer" ></tx:method> </tx:attributes> </tx:advice> <!-- aop配置定义切面 和切点的信息--> <aop:config> <!-- 定义切点:哪些类的哪些方法应用增强 --> <aop:pointcut expression="execution(* com.js.demo2.AccountService+.*(..))" id="mypointcut"/> <!-- 定义切面 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="mypointcut"/> </aop:config> </beans>
第3步:编写测试类
package com.js.demo2; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * 测试转账功能 * @author hdb * */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class C3P0DataSourceAccountTest { @Autowired @Qualifier("accountService") private AccountService accountService; @Test public void demo(){ //完成转账 accountService.transfer("aaa", "bbb", 100d); } }
测试有异常发生的情况下,事务的管理是否生效,详细参考上面。
事务管理之声明式事务-基于注解
第1步:导入相应的jar包。
第2步:配置事务管理器、并开启注解的事务管理
<?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:util="http://www.springframework.org/schema/util" 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/util http://www.springframework.org/schema/util/spring-util.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"> <!-- 纯用注解配置bean的情况下context:annotation-config可以省略,当混合使用xml和注解配置bean的时候就不能省略了--> <!-- 作用是使@Resource、@PostConstruct、@PreDestroy、@AutoWired生效--> <context:annotation-config></context:annotation-config> <!-- 第1种,配置属性文件:配置bean,暂时注释 --> <!-- <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location" value="classpath:jdbc.properties"></property> </bean> --> <!-- 第2种,配置属性文件:引入context约束,然后解析属性文件 --> <context:property-placeholder location="classpath:jdbc.properties"/> <!-- 配置C3P0连接池 --> <bean id="C3P0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driver}"></property> <property name="jdbcUrl" value="${jdbc.url}"></property> <property name="user" value="${jdbc.name}"></property> <property name="password" value="${jdbc.password}"></property> </bean> <!-- 定义JDBC的模板类 --> <bean id="C3P0JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="C3P0DataSource"></property> </bean> <!-- 业务层类 --> <bean id="accountService" class="com.js.demo2.AccountServiceImpl"> <!-- 在业务层注入Dao --> <property name="accountDao" ref="accountDao"></property> </bean> <!-- 持久层类 --> <bean id="accountDao" class="com.js.demo2.AccountDaoImpl"> <!-- 注入连接池对象,通过连接池对象去创建JDBC模板 --> <property name="dataSource" ref="C3P0DataSource"></property> </bean> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 需要注入连接池,通过连接池获得连接 --> <property name="dataSource" ref="C3P0DataSource"></property> </bean> <!-- 开启注解的事务管理 --> <tx:annotation-driven transaction-manager="transactionManager"/> </beans>
第3步:在Service上使用注解AccountServiceImpl:@Transactional
package com.js.demo2; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; @Transactional public class AccountServiceImpl implements AccountService{ private AccountDao accountDao; //添加事务模板属性 private TransactionTemplate transactionTemplate; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } public void setTransactionTemplate(TransactionTemplate transactionTemplate) { this.transactionTemplate = transactionTemplate; } /** * 转账的方法 * @param from:从哪转出 * @param to:转入目标 * @param money:转账金额 */ public void transfer(final String from,final String to,final double money) { //编写事务 transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { try{ accountDao.out(from, money); // int i = 1/0; //制造异常 accountDao.in(to, money); System.out.println("用户"+from+"向用户"+to+"转账"+money+"元"); }catch (Exception e) { e.printStackTrace(); } } }); } }
第4步:编写测试类
package com.js.demo2; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * 测试转账功能 * @author hdb * */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class C3P0DataSourceAccountTest { @Autowired @Qualifier("accountService") private AccountService accountService; @Test public void demo(){ //完成转账 accountService.transfer("aaa", "bbb", 100d); } }
测试有异常发生的情况下,事务的管理是否生效,详细参考上面。