一、事物的基本概念
事物是并发控制的基本单位,所谓事物,就是指一个完整的操作序列,该序列中的操作作为一个不可分割的工作单元,要么都执行,要么都不执行
事物的四大特性:
- 原子性:Attomicity,是指要么都提交,要么都不提交,保持一致。
- 一致性:Cononsistency,事物前后数据的完整性保持一致。
- 隔离性:Isolation,并发事务执行之间无影响,在一个事务内部的操作对其他事务是不产生影响,这需要事务隔离级别来指定隔离性。
- 持久性:Durability,指一个事务一旦被提交,它对数据库中数据的改变就是永久性的。
二、Spring对事物的支持
事物为数据准确性提供了有效保障,但在实际开发中如果每次操作数据库都要手动提交回滚事物,不仅增加了大量重复工作,也更容易引发错误。所以,为解决企业级开发的问题,Spring框架提供了对事物管理的支持。Spring并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现,下面我们来看一下这种组织架构
三、Spring事务管理器
接口org.springframework.transaction.PlatformTransactionManager,通过这个接口,Spring为各个平台如JDBC(IBatis)、Hibernate、JPA等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。此接口的内容如下:
Public interface PlatformTransactionManager()...{
// 由TransactionDefinition得到TransactionStatus对象
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
// 提交
Void commit(TransactionStatus status) throws TransactionException;
// 回滚
Void rollback(TransactionStatus status) throws TransactionException;
}
从这里可知具体的具体的事务管理机制对Spring来说是透明的,它并不关心那些,那些是对应各个平台需要关心的,所以Spring事务管理的一个优点就是为不同的事务API提供一致的编程模型,如JTA、JDBC(IBatis)、Hibernate、JPA。下面分别介绍各个平台框架实现事务管理的机制。
- JDBC/IBatis:org.springframework.jdbc.datasource.DataSourceTransactionManager,实际上,DataSourceTransactionManager是通过调用java.sql.Connection来管理事务,而后者是通过DataSource获取到的。通过调用连接的commit()方法来提交事务,同样,事务失败则通过调用rollback()方法进行回滚。
- Hibernate:org.springframework.orm.hibernate5.HibernateTransactionManager,HibernateTransactionManager的实现细节是它将事务管理的职责委托给org.hibernate.Transaction对象,而后者是从Hibernate Session中获取到的。当事务成功完成时,HibernateTransactionManager将会调用Transaction对象的commit()方法,反之,将会调用rollback()方法。
- Java持久化API事务(JPA):org.springframework.orm.jpa.JpaTransactionManager,JpaTransactionManager只需要装配一个JPA实体管理工厂(javax.persistence.EntityManagerFactory接口的任意实现)。JpaTransactionManager将与由工厂所产生的JPA EntityManager合作来构建事务。
- Java原生API事务(JTA):org.springframework.transaction.jta.JtaTransactionManager,JtaTransactionManager将事务管理的责任委托给javax.transaction.UserTransaction和javax.transaction.TransactionManager对象,其中事务成功完成通过UserTransaction.commit()方法提交,事务失败通过UserTransaction.rollback()方法回滚。
- JAVA应用非EJB标准(Jdo):opg.springframework.jdo.JdoTransactionManager
四、事物基本属性
上面组织架构图讲到的事务管理器接口PlatformTransactionManager通过getTransaction(TransactionDefinition definition)方法来得到事务,这个方法里面的参数是TransactionDefinition,这个接口就定义了一些基本的事务属性。 那么什么是事务属性呢?事务属性可以理解成事务的一些基本配置,描述了事务策略如何应用到方法上。事务属性包含了5个方面:
- 传播行为:事务的第一个方面是传播行为(propagation behavior)。当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。Spring定义了七种传播行为
属性 | 值 | 解释 |
---|---|---|
PROPAGATION_REQUIRED | 0 | 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择,也是Spring默认的事务的传播。 |
PROPAGATION_SUPPORTS | 1 | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 2 | 支持当前事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 3 | 新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED | 4 | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 5 | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 6 | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。 |
- 隔离级别:在企业级应用中,多用户访问数据库是常见的场景,这就是所谓的事务的并发。在许多事务处理同一个数据时,如果没有采取有效的隔离机制,那么并发处理数据时,会带来一些的问题:
1.脏读:一个事务读到另一个事务未提交的更新数据。
2.不可重复读:一个事务两次读取同一行的数据,结果得到不同状态的结果,中间正好另一个事务更新了该数据,两次结果相异,不可被信任。
3.幻读:一个事务执行两次查询,第二次结果集包含第一次中没有或某些行已经被删除的数据,造成两次结果不一致,只是另一个事务在这两次查询中间插入或删除了数据造成的。
4.丢失更新:撤消一个事务时,把其它事务已提交的更新的数据覆盖了。
为了解决事物并发所引起的一些问题,就有了隔离级别的概念,下面我们介绍事物的五种隔离级别
属性 | 值 | 解释 |
---|---|---|
ISOLATION_DEFAULT | -1 | 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。另外四个与JDBC的隔离级别相对应 |
ISOLATION_READ_UNCOMMITTED | 1 | 这是事务最低的隔离级别,它充许另外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻读。 |
ISOLATION_READ_COMMITTED | 2 | 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。 |
ISOLATION_REPEATABLE_READ | 4 | 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。 |
ISOLATION_SERIALIZABLE | 8 | 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻读。 |
MySQL默认采用REPEATABLE_READ隔离级别;Oracle默认采用READ_COMMITTED隔离级别
只读:事务的第三个特性是它是否为只读事务。如果事务只对后端的数据库进行该操作,数据库可以利用事务的只读特性来进行一些特定的优化。通过将事务设置为只读,你就可以给数据库一个机会,让它应用它认为合适的优化措施。
事务超时:为了使应用程序很好地运行,事务不能运行太长的时间。因为事务可能涉及对后端数据库的锁定,所以长时间的事务会不必要的占用数据库资源。事务超时就是事务的一个定时器,在特定时间内事务如果没有执行完毕,那么就会自动回滚,而不是一直等待其结束。
回滚规则:回滚规则定义了哪些异常会导致事务回滚而哪些不会。默认情况下,事务只有遇到运行期异常时才会回滚,而在遇到检查型异常时不会回滚(这一行为与EJB的回滚行为是一致的),但是你可以声明事务在遇到特定的检查型异常时像遇到运行期异常那样回滚。同样,你还可以声明事务遇到特定的异常不回滚,即使这些异常是运行期异常。
五、事物的状态
上面组织架构图讲到的调用PlatformTransactionManager接口的getTransaction()的方法得到的是TransactionStatus接口的一个实现,这个接口的内容如下
void flush(); //如果适用的话,这个方法用于刷新底层会话中的修改到数据库,例如,所有受影响的Hibernate/JPA会话。
boolean hasSavepoint(); // 是否有恢复点
boolean isCompleted(); // 是否已完成
boolean isNewTransaction(); // 是否是新的事务
boolean isRollbackOnly(); // 是否为只回滚
void setRollbackOnly(); // 设置为只回滚
可以发现这个接口描述的是一些处理事务提供简单的控制事务执行和查询事务状态的方法,在回滚或提交的时候需要应用对应的事务状态。
六、编程式事物管理
Spring提供两种编程式事物管理方式,分别是使用TransactionTemplate和直接使用PlatformTransactionManager,对于编程式事物管理,Spring推荐使用TransactionTemplate。
- 使用TransactionTemplate模板
采用TransactionTemplate和采用其他Spring模板(如JdbcTempalte)是一样的方法。它使用回调方法,把应用程序从处理取得和释放资源中解脱出来。如同其他模板,TransactionTemplate是线程安全的。接下来我们看下使用方法:
Dao
package com.sitech.test15;
import org.springframework.jdbc.core.JdbcTemplate;
public class TransferAccountDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public int change(Double num, String userName) {
String sql = " UPDATE user SET money = money + ? WHERE username = ? ";
return jdbcTemplate.update(sql, new Object[]{num, userName});
}
}
Service
package com.sitech.test15;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
public class TransferAccountService {
TransferAccountDao transferAccountDao;
TransactionTemplate transactionTemplate;
public void setTransferAccountDao(TransferAccountDao transferAccountDao) {
this.transferAccountDao = transferAccountDao;
}
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
public void transferAccount(final String userName1, final String userName2, final Double money) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
public void doInTransactionWithoutResult(TransactionStatus arg0) {
int int2 = 0;
int int1 = transferAccountDao.change(money, userName1);
if (1 == int1) {
// 事物测试
transferAccountDao = null;
int2 = transferAccountDao.change(-money, userName2);
}
System.out.println(int2);
}
});
}
}
资源文件配置DataSource.properties
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc\:mysql\://127.0.0.1\:3306/chndb
jdbc.username=root
jdbc.password=root
initPoolSize=5
jdbc.maxpoolsize=60
Spring文件配置
<!-- 配置资源文件路径 -->
<context:property-placeholder location="classpath:DataConnection.properties"/>
<!-- 配置C3P0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 注入属性 -->
<property name="driverClass" value="${jdbc.driverClassName}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 配置JdbcTemplate模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置编程式事物模板 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
</bean>
<!-- 配置Dao -->
<bean id="transferAccountDao" class="com.sitech.test15.TransferAccountDao">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<!-- 配置Service -->
<bean id="transferAccountService" class="com.sitech.test15.TransferAccountService">
<property name="transferAccountDao" ref="transferAccountDao"></property>
<property name="transactionTemplate" ref="transactionTemplate"></property>
</bean>
Test,测试当transferAccountDao 设置为null时事物会回滚,这里由于篇幅原因就不对结果进行展示了
package com.sitech.test15;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("test15.xml");
TransferAccountService transferAccountService = (TransferAccountService) applicationContext.getBean("transferAccountService");
transferAccountService.transferAccount("Jack", "Lucy", 10.0);
}
}
- 使用PlatformTransactionManager底层实现类DataSourceTransactionManager
Dao
package com.sitech.test16;
import org.springframework.jdbc.core.JdbcTemplate;
public class TransferAccountDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public int change(Double num, String userName) throws Exception {
String sql = " UPDATE user SET money = money + ? WHERE username = ? ";
return jdbcTemplate.update(sql, new Object[]{num, userName});
}
}
Service
package com.sitech.test16;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
public class TransferAccountService {
TransferAccountDao transferAccountDao;
DataSourceTransactionManager dataSourceTransactionManager;
DefaultTransactionDefinition defaultTransactionDefinition;
public void setTransferAccountDao(TransferAccountDao transferAccountDao) {
this.transferAccountDao = transferAccountDao;
}
public void setDataSourceTransactionManager(DataSourceTransactionManager dataSourceTransactionManager) {
this.dataSourceTransactionManager = dataSourceTransactionManager;
}
public void setDefaultTransactionDefinition(
DefaultTransactionDefinition defaultTransactionDefinition) {
this.defaultTransactionDefinition = defaultTransactionDefinition;
}
public void transferAccount(String userName1, String userName2, Double money) {
TransactionStatus status = dataSourceTransactionManager.getTransaction(defaultTransactionDefinition);
try {
int int2 = 0;
int int1 = transferAccountDao.change(money, userName1);
if (1 == int1) {
// 事物测试
transferAccountDao = null;
int2 = transferAccountDao.change(-money, userName2);
}
System.out.println(int2);
dataSourceTransactionManager.commit(status);
} catch (Exception e) {
dataSourceTransactionManager.rollback(status);
e.printStackTrace();
}
}
}
资源文件配置DataSource.properties
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc\:mysql\://127.0.0.1\:3306/chndb
jdbc.username=root
jdbc.password=root
initPoolSize=5
jdbc.maxpoolsize=60
Spring文件配置
<!-- 配置资源文件路径 -->
<context:property-placeholder location="classpath:DataConnection.properties"/>
<!-- 配置C3P0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 注入属性 -->
<property name="driverClass" value="${jdbc.driverClassName}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 配置JdbcTemplate模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务管理器 -->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务定义模型 -->
<bean id="defaultTransactionDefinition" class="org.springframework.transaction.support.DefaultTransactionDefinition">
<property name="propagationBehavior" value="0"></property>
</bean>
<!-- 配置Dao -->
<bean id="transferAccountDao" class="com.sitech.test16.TransferAccountDao">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<!-- 配置Service -->
<bean id="transferAccountService" class="com.sitech.test16.TransferAccountService">
<property name="transferAccountDao" ref="transferAccountDao"></property>
<property name="dataSourceTransactionManager" ref="dataSourceTransactionManager"></property>
<property name="defaultTransactionDefinition" ref="defaultTransactionDefinition"></property>
</bean>
Test,测试当transferAccountDao 设置为null时事物会回滚,这里由于篇幅原因就不对结果进行展示了
package com.sitech.test15;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("test15.xml");
TransferAccountService transferAccountService = (TransferAccountService) applicationContext.getBean("transferAccountService");
transferAccountService.transferAccount("Jack", "Lucy", 10.0);
}
}
七、声明式事物管理
声明式事务管理有两种常用的方式,一种是基于XML配置文件,一种是基于@Transactional注解,随着Spring和Java的版本越来越高,大家越趋向于使用注解的方式
- 基于XML配置文件
Dao
package com.sitech.test17;
import org.springframework.jdbc.core.JdbcTemplate;
public class TransferAccountDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public int change(Double num, String userName) {
String sql = " UPDATE user SET money = money + ? WHERE username = ? ";
return jdbcTemplate.update(sql, new Object[]{num, userName});
}
}
Service
package com.sitech.test17;
public class TransferAccountService {
TransferAccountDao transferAccountDao;
public void setTransferAccountDao(TransferAccountDao transferAccountDao) {
this.transferAccountDao = transferAccountDao;
}
public void transferAccount(String userName1, String userName2, Double money) {
int int2 = 0;
int int1 = transferAccountDao.change(money, userName1);
if (1 == int1) {
// 事物测试
transferAccountDao = null;
int2 = transferAccountDao.change(-money, userName2);
}
}
}
资源文件配置DataSource.properties
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc\:mysql\://127.0.0.1\:3306/chndb
jdbc.username=root
jdbc.password=root
initPoolSize=5
jdbc.maxpoolsize=60
Spring文件配置
<!-- 配置资源文件路径 -->
<context:property-placeholder location="classpath:DataConnection.properties"/>
<!-- 配置C3P0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 注入属性 -->
<property name="driverClass" value="${jdbc.driverClassName}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 配置JdbcTemplate模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<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>
<!-- 设置方法匹配规则,支持*匹配 -->
<tx:method name="transfer*"/>
</tx:attributes>
</tx:advice>
<!-- 配置Spring AOP切面 -->
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut id="transferAccountPointcut" expression="execution(* com.sitech.test17.TransferAccountService.*(..))"/>
<!-- 配置切面 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="transferAccountPointcut"/>
</aop:config>
<!-- 配置Dao -->
<bean id="transferAccountDao" class="com.sitech.test17.TransferAccountDao">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<!-- 配置Service -->
<bean id="transferAccountService" class="com.sitech.test17.TransferAccountService">
<property name="transferAccountDao" ref="transferAccountDao"></property>
</bean>
Test,测试当transferAccountDao 设置为null时事物会回滚,这里由于篇幅原因就不对结果进行展示了
package com.sitech.test17;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("test17.xml");
TransferAccountService transferAccountService = (TransferAccountService) applicationContext.getBean("transferAccountService");
transferAccountService.transferAccount("Jack", "Lucy", 10.0);
}
}
- 基于@Transactional注解
@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。
虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。
默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。
属性 | 类型 | 解释 |
---|---|---|
value | String | 可选的限定描述符,指定使用的事务管理器 |
propagation | enum: Propagation | 可选的事务传播行为设置 |
isolation | enum: Isolation | 可选的事务隔离级别设置 |
readOnly | boolean | 读写或只读事务,默认读写 |
timeout | int (in seconds granularity) | 事务超时时间设置 |
rollbackFor | Class对象数组,必须继承自Throwable | 导致事务回滚的异常类数组 |
rollbackForClassName | 类名数组,必须继承自Throwable | 导致事务回滚的异常类名字数组 |
noRollbackFor | Class对象数组,必须继承自Throwable | 不会导致事务回滚的异常类数组 |
noRollbackForClassName | 类名数组,必须继承自Throwable | 不会导致事务回滚的异常类名字数组 |
Dao
package com.sitech.test18;
import javax.annotation.Resource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository(value="transferAccountDao")
public class TransferAccountDao {
@Resource(name="jdbcTemplate")
private JdbcTemplate jdbcTemplate;
public int change(Double num, String userName) {
String sql = " UPDATE user SET money = money + ? WHERE username = ? ";
return jdbcTemplate.update(sql, new Object[]{num, userName});
}
}
Service
package com.sitech.test18;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service(value="transferAccountService")
public class TransferAccountService {
@Resource(name="transferAccountDao")
TransferAccountDao transferAccountDao;
@Transactional(rollbackFor=NullPointerException.class)
public void transferAccount(String userName1, String userName2, Double money) {
int int2 = 0;
int int1 = transferAccountDao.change(money, userName1);
if (1 == int1) {
// 事物测试
//transferAccountDao = null;
int2 = transferAccountDao.change(-money, userName2);
System.out.println(int2);
}
}
}
资源文件配置DataSource.properties
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc\:mysql\://127.0.0.1\:3306/chndb
jdbc.username=root
jdbc.password=root
initPoolSize=5
jdbc.maxpoolsize=60
Spring文件配置
<!-- 配置资源文件路径 -->
<context:property-placeholder location="classpath:DataConnection.properties"/>
<!-- 配置C3P0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 注入属性 -->
<property name="driverClass" value="${jdbc.driverClassName}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 配置JdbcTemplate模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--
到包里扫描类、方法、属性是否有注解
com.sitech只改包下的所有类,也可以写成com
-->
<context:component-scan base-package="com.sitech.test18"></context:component-scan>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 开启注解事务 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
Test,测试当transferAccountDao 设置为null时事物会回滚,这里由于篇幅原因就不对结果进行展示了
package com.sitech.test18;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("test18.xml");
TransferAccountService transferAccountService = (TransferAccountService) applicationContext.getBean("transferAccountService");
transferAccountService.transferAccount("Jack", "Lucy", 10.0);
}
}