Spring编程式事务与Spring声明式事务

    在了解Spring事务之前,先了解一下事务、事务的特性、事务隔离级别、事务的传播行为。

    事务:事务是逻辑上的一组原子操作,要么全部成功,要么全部失败。

    事务的4大特性(ACID):原子性、一致性、隔离性、持久性。

  1.  原子性:原子性是指事务操作是一个不可分割的一个整体,事务中的操作要么一起成功,要么一起失败。
  2. 一致性:是指事务操作前后的数据完整性必须保持一致。例如:张三账户1000,李四账户1000,经过转账后,他俩账户的总额是不变的。
  3. 隔离性:多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务干扰,多个用户的数据要进行隔离。
  4. 持久性:一个事务一旦被提交,它对数据库中数据的改变是永久性,即使数据库也不应对其有任何影响。

事务的隔离级别

事务的隔离级别的不同,能够有效的避免数据的脏读、不可重复读、幻读。

脏读:一个事务读取到了被另一个事务改写还未提交的事务,一旦这些数据被回滚,那么读取到的数据都是无效的。

不可重复读:在一个事务中,多次读取到的数据是不相同的。

幻读:再一个事务读取完几行数据后,另一个事务又插入了几行数据,并提交,这时,如果该事务再次读取时,就会发生和原来数据不一致的数据,发生了幻读。

事务的隔离级别有一下几种:


注意: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的注解

    

猜你喜欢

转载自blog.csdn.net/qq_36795474/article/details/80184523