在了解Spring事务之前,先了解一下事务、事务的特性、事务隔离级别、事务的传播行为。
事务:事务是逻辑上的一组原子操作,要么全部成功,要么全部失败。
事务的4大特性(ACID):原子性、一致性、隔离性、持久性。
- 原子性:原子性是指事务操作是一个不可分割的一个整体,事务中的操作要么一起成功,要么一起失败。
- 一致性:是指事务操作前后的数据完整性必须保持一致。例如:张三账户1000,李四账户1000,经过转账后,他俩账户的总额是不变的。
- 隔离性:多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务干扰,多个用户的数据要进行隔离。
- 持久性:一个事务一旦被提交,它对数据库中数据的改变是永久性,即使数据库也不应对其有任何影响。
事务的隔离级别
事务的隔离级别的不同,能够有效的避免数据的脏读、不可重复读、幻读。
脏读:一个事务读取到了被另一个事务改写还未提交的事务,一旦这些数据被回滚,那么读取到的数据都是无效的。
不可重复读:在一个事务中,多次读取到的数据是不相同的。
幻读:再一个事务读取完几行数据后,另一个事务又插入了几行数据,并提交,这时,如果该事务再次读取时,就会发生和原来数据不一致的数据,发生了幻读。
事务的隔离级别有一下几种:
注意:Oracle默认的隔离级别是READ_COMMITTED, Mysql默认的隔离级别是REPEATABLE_READ
事务的隔离级别
我们通常会在service层添加事务,假设我们的web层要调用两个service层中的方法,而每一service层中可能都会有自己的事务,在这种情况下,我们到底该用哪个事务呢,在这种情况下,就会用到事务的隔离级别。事务的隔离级别主要有一下7种。
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: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-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
">
<context:property-placeholder location="classpath:/jdbc.properties"/>
<!--c3p0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${driverName}"/>
<property name="jdbcUrl" value="${url}"/>
<property name="user" value="${name}"/>
<property name="password" value="${password}"/>
</bean>
<!--业务层信息 -->
<bean id="accountService" class="service.AccountService">
<property name="template" ref="transactionTemplate"/>
<property name="accountDao" ref="accountDao"/>
</bean>
<!-- 为DAO注入连接池,创建jdbc模板 -->
<bean id="accountDao" class="dao.AccountDAO">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事务管理器(主要用于事务管理) -->
<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"/>
</bean>
</beans>
service
public class AccountService {
//注入事务模板
private TransactionTemplate template;
public void setTemplate(TransactionTemplate template) {
this.template = template;
}
private AccountDAO accountDao;
public void setAccountDao(AccountDAO accountDao) {
this.accountDao = accountDao;
}
public void transfer(final String in,final String out,final double money){
template.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus arg0) {
accountDao.outMoney(out, money);
int i=1/0;
accountDao.inMoney(in, money);
}
});
}
}
dao
public class AccountDAO extends JdbcDaoSupport {
public void inMoney(String in,Double money){
String sql="update account set money=money+? where name=?";
this.getJdbcTemplate().update(sql, money,in);
}
public void outMoney(String out,Double money){
String sql="update account set money=money-? where name=?";
this.getJdbcTemplate().update(sql, money,out);
}
}
Spring声明式事务管理
1、基于TransactionProxyFactoryBean的方式
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: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-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
">
<context:property-placeholder location="classpath:/jdbc.properties"/>
<!--c3p0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${driverName}"/>
<property name="jdbcUrl" value="${url}"/>
<property name="user" value="${name}"/>
<property name="password" value="${password}"/>
</bean>
<!--业务层信息 -->
<bean id="accountService" class="service.AccountService2">
<property name="accountDao" ref="accountDao"/>
</bean>
<!-- 为DAO注入连接池,创建jdbc模板 -->
<bean id="accountDao" class="dao.AccountDAO">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 为现有的service产生代理 -->
<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 :事务的传播行为
ISOLATIION :事务的隔离级别
readOnly : 只读(不可以进行增加,删除,修改)
-Exception :发生哪些异常回滚事务
+Exception :发生哪些异常不会滚
-->
<!-- <prop key="insert*">PROPAGATION_REQUIRED</prop> 对于所有的insert开头的方法,添加PROPAGATION_REQUIRED属性
<prop key="update*">PROPAGATION_REQUIRED</prop>对于所有的update开头的方法,添加PROPAGATION_REQUIRED属性
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>对于所有的其他的方法,添加PROPAGATION_REQUIRED和只读属性 -->
<prop key="transfer">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
</beans>
service
public class AccountService2 {
private AccountDAO accountDao;
public void setAccountDao(AccountDAO accountDao) {
this.accountDao = accountDao;
}
public void transfer(String in,String out,double money){
accountDao.outMoney(out, money);
int i=1/0;
accountDao.inMoney(in, money);
}
}
dao
public class AccountDAO extends JdbcDaoSupport {
public void inMoney(String in,Double money){
String sql="update account set money=money+? where name=?";
this.getJdbcTemplate().update(sql, money,in);
}
public void outMoney(String out,Double money){
String sql="update account set money=money-? where name=?";
this.getJdbcTemplate().update(sql, money,out);
}
}
测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext2.xml")
public class SpringTest2 {
@Resource(name="accountServiceProxy")//注意这里必须为AccountService2进行注入代理类
private AccountService2 accountService;
@Test
public void test(){
accountService.transfer("aaa", "bbb",200);
}
}
2、 基于AspectJ的XML事务管理方式
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: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-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
">
<!--基于AspectJ的XML声明式开发 -->
<context:property-placeholder location="classpath:/jdbc.properties"/>
<!--c3p0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${driverName}"/>
<property name="jdbcUrl" value="${url}"/>
<property name="user" value="${name}"/>
<property name="password" value="${password}"/>
</bean>
<!--业务层信息 -->
<bean id="accountService" class="service.AccountService2">
<property name="accountDao" ref="accountDao"/>
</bean>
<!-- 为DAO注入连接池,创建jdbc模板 -->
<bean id="accountDao" class="dao.AccountDAO">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事务的通知:(事务的增强) -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--
propagation :事务的传播行为
isolation :事务的隔离级别
read-only :只读(不能在事务中有增,删,改)
rollback-for :发生异常回滚
no-rollback-for :发生异常不回滚
timeout :过期日期
-->
<tx:method name="transfer" propagation="REQUIRED"/><!--这里的method是对方法增强 -->
</tx:attributes>
</tx:advice>
<!--配置问题 -->
<aop:config>
<aop:pointcut expression="execution(* service.AccountService2.*(..))" id="pointcut"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
</aop:config>
</beans>
service
public class AccountService2 {
private AccountDAO accountDao;
public void setAccountDao(AccountDAO accountDao) {
this.accountDao = accountDao;
}
public void transfer(String in,String out,double money){
accountDao.outMoney(out, money);
int i=1/0;
accountDao.inMoney(in, money);
}
}
dao
public class AccountDAO extends JdbcDaoSupport {
public void inMoney(String in,Double money){
String sql="update account set money=money+? where name=?";
this.getJdbcTemplate().update(sql, money,in);
}
public void outMoney(String out,Double money){
String sql="update account set money=money-? where name=?";
this.getJdbcTemplate().update(sql, money,out);
}
}
测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext3.xml")
public class SpringTest3 {
@Resource(name="accountService")
private AccountService2 accountService2;
@Test
public void test(){
accountService2.transfer("aaa", "bbb",200);
}
}
3、基于注解的事务管理
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: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-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
">
<!--基于注解声明式开发 -->
<context:property-placeholder location="classpath:/jdbc.properties"/>
<!--c3p0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${driverName}"/>
<property name="jdbcUrl" value="${url}"/>
<property name="user" value="${name}"/>
<property name="password" value="${password}"/>
</bean>
<!--业务层信息 -->
<bean id="accountService" class="service.AccountService3">
<property name="accountDao" ref="accountDao"/>
</bean>
<!-- 为DAO注入连接池,创建jdbc模板 -->
<bean id="accountDao" class="dao.AccountDAO">
<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>
service
package service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import dao.AccountDAO;
/**
*
* @Transaction注解中的属性:
* propagation :事务的传播行为
* isolation :事务的隔离级别
* readOnly :只读
* rollbackFor :发生哪些异常回滚
* noRollbackFor :发生哪些异常不回滚
*要在你所需要加入事务的service层代码中加入事务
*/
@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.DEFAULT,readOnly=false)//当然如果不在注解里写这些属性,事务会有默认的属性
public class AccountService3 {
private AccountDAO accountDao;
public void setAccountDao(AccountDAO accountDao) {
this.accountDao = accountDao;
}
public void transfer(String in,String out,double money){
accountDao.outMoney(out, money);
int i=1/0;
accountDao.inMoney(in, money);
}
}
测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext4.xml")
public class SpringTest4 {
@Resource(name="accountService")
private AccountService3 accountService2;
@Test
public void test(){
accountService2.transfer("aaa", "bbb",200);
}
}
总结:Spring将事务分为两类:
1、编程式事务管理:手动编写代码进行事务管理(很少使用)。
2、声明式事务管理:
a.基于TransactionProxyFactoryBean的方式(很少使用),需要为每个事务管理的类,配置一 个 TransactionProxyFactoryBean进行增强。
b.基于AspectJ的XML方式(经常使用)
c.基于注解方式(经常使用)
配置简单,需要在业务层上添加一个@Transactional的注解