一、Spring事务管理 

      在JavaEE分层开发中,事务管理代码放到业务层

1、 事务管理相关API

      PlatformTransactionManager  平台事务管理器

      * void commit(TransactionStatus status)  提交事务
      * TransactionStatus getTransaction(TransactionDefinition definition) 根据事务定义信息,获得当前状态 
      * void rollback(TransactionStatus status)  回滚事务


      TransactionDefinition  事务定义信息 (配置信息 来自xml配置文件 和 注解)

      包括 (隔离、传播、超时、只读)
      * ISOLATION_xxx 事务隔离级别 
      * PROPAGATION_xxx  事务传播行为 
      * int getTimeout()  获得超时信息
      * boolean isReadOnly()  判断事务是否只读


      TransactionStatus 事务具体运行状态
      * 每一个时刻点 事务具体状态信息


      关系: PlatformTransactionManager 根据 TransactionDefinition 进行事务管理, 管理过程中事务存在多种状态, 每个状态信息通过 TransactionStatus 表示


2、 PlatformTransactionManager 接口详解

1) 不同平台事务管理器实现

      org.springframework.jdbc.datasource.DataSourceTransactionManager使用Spring JDBC或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实现来管理事务,在一个事务跨越多个资源时必须使用

      * 针对不同持久层技术,选用对应事务管理器

2) TransactionDefinition 事务隔离级别

      事务四大特性 : ACID  原子性、一致性、隔离性、持久性

      隔离性引发并发问题: 脏读、 不可重复读、 虚读

            * 脏读 一个事务读取另一个事务 未提交数据
            * 不可重复读 一个事务读取另一个事务 已经提交 update 数据
            * 虚读  一个事务读取另一个事务 已经提交 insert 数据

      事务隔离级别 为了解决 事务隔离性引发 问题

            * DEFAULT 默认级别  mysql REPEATABLE_READ 、 oracle READ_COMMITTED
            * READ_UNCOMMITED  导致所有问题发生
            * READ_COMMITTED 防止脏读、 发生不可重复读和虚读
            * REPEATABLE_READ 防止脏读、不可重复读,发生虚读
            * SERIALIZABLE 防止所有并发问题 

3) TransactionDefinition 事务的传播行为 
      * 不是JDBC 规范定义 
      * 传播行为 针对实际开发中问题 

      传播行为解决问题: 一个业务层事务 调用 另一个业务层事务,事务之间关系如何处理

      七种传播行为

      PROPAGATION_REQUIRED 支持当前事务,如果不存在 就新建一个
            * 删除客户 删除订单, 处于同一个事务,如果 删除订单失败,删除客户也要回滚

      PROPAGATION_SUPPORTS 支持当前事务,如果不存在,就不使用事务
            * 如果第一个操作没有开启事务,就不使用事务来操作(如果删除客户没有使用事务,那么删除订单也不使用事务)
      PROPAGATION_MANDATORY 支持当前事务,如果不存在,抛出异常

      PROPAGATION_REQUIRES_NEW 如果有事务存在,挂起当前事务,创建一个新的事务
            * 生成订单, 发送通知邮件, 通知邮件会创建一个新的事务,如果邮件失败, 不影响订单生成
      PROPAGATION_NOT_SUPPORTED 以非事务方式运行,如果有事务存在,挂起当前事务
      PROPAGATION_NEVER 以非事务方式运行,如果有事务存在,抛出异常
      PROPAGATION_NESTED 如果当前事务存在,则嵌套事务执行
            * 依赖于 JDBC3.0 提供 SavePoint 技术 
      * 删除客户 删除订单, 在删除客户后, 设置SavePoint, 执行删除订单,删除订单和删除客户在同一个事务 ,删除订单失败, 事务回滚 SavePoint , 由用户控制是事务提交 还是 回滚


      重点:
      PROPAGATION_REQUIRED 一个事务, 要么都成功,要么都失败 
      PROPAGATION_REQUIRES_NEW 两个不同事务,彼此之间没有关系  一个事务失败了 不影响另一个事务 
      PROPAGATION_NESTED  一个事务, 在A事务 调用 B过程中, B失败了, 回滚事务到 之前SavePoint , 用户可以选择提交事务或者回滚事务


3、Spring事务管理方式

      1)编程式事务管理

      在代码中,通过TransactionTemplate手动进行事务管理,在实际开发中很少被用到。

      2)声明式事务管理

      在配置文件中,对Bean的方法进行事务管理,基于AOP思想,无需写代码,开发中主流使用。


4、Spring通过编程式进行事务管理

      第一步、创建账户数据表

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');
     第二步、编写转账操作程序(数据库操作JdbcTemplate)

package lsq.spring.transaction.a_program;

public interface AccountDao {
	/**
	 * 转出
	 * @param outAccount  转出账户
	 * @param money       转出金额
	 */
	public void outMoney(String outAccount, double money);
	
	/**
	 * 转入
	 * @param inAccount   转入账户
	 * @param money       转入金额
	 */
	public void inMoney(String inAccount, double money);
}
package lsq.spring.transaction.a_program;

import org.springframework.jdbc.core.support.JdbcDaoSupport;

//Dao实现类
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {

	@Override
	public void outMoney(String outAccount, double money) {
		String sql = "update account set money = money - ? where name = ?";
		this.getJdbcTemplate().update(sql, money, outAccount);
	}

	@Override
	public void inMoney(String inAccount, double money) {
		String sql = "update account set money = money + ? where name = ?";
		this.getJdbcTemplate().update(sql, money, inAccount);
	}

}
package lsq.spring.transaction.a_program;
//账户管理
public interface AccountService {
	/**
	 * 转账
	 * @param outAccount  转出账户
	 * @param inAccount   转入账户
	 * @param money       转账金额
	 */
	public void transfer(String outAccount, String inAccount, double money);
}
package lsq.spring.transaction.a_program;

import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

//业务层实现
public class AccountServiceImpl implements AccountService {
	//在Service中注入Dao
	private AccountDao accountDao;

	//在Service中注入Template模板
	private TransactionTemplate transactionTemplate;

	public void setAccountDao(AccountDao accountDao) {
		this.accountDao = accountDao;
	}
	
	public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
		this.transactionTemplate = transactionTemplate;
	}

	@Override
	public void transfer(final String outAccount, final String inAccount, final double money) {
		//使用事务模板 管理事务
		transactionTemplate.execute(new TransactionCallbackWithoutResult() {
			@Override
			protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
				accountDao.outMoney(outAccount, money);
				int d = 1/0;
				accountDao.inMoney(inAccount, money);
			}
		});
	}
}
      第三步、配置applicationContext.xml:

      ☆:如果没有使用任何事务管理 , JdbcTemplate DAO 每个操作 就是单独的事务 

	<!-- 导入外部属性文件 -->
	<context:property-placeholder location="classpath:jdbc.properties"/>
	
	<!-- 配置数据库连接池 -->
	<bean id="dataSource" 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.user}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean>
	
	<!-- Dao -->
	<bean id="accountDao" class="lsq.spring.transaction.a_program.AccountDaoImpl">
		<!-- 将连接池注入到Dao,JdbcDaoSupport会自动创建JdbcTemplate对象 -->
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 先把管理器注入给模板,再把模板注入给业务层Service -->
	<!-- 配置事务管理器(真正管理事务的) -->
	<!-- 事务管理器管理事务的原理:把连接池交给管理器,管理器从连接池里取得一个连接,
		然后调用连接的setAutoCommit(false)启动事务,在以后的操作中都从管理器中获得那同一个连接,就可以保证操作在同一个连接中。 -->
	<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"></property>
	</bean>
	
	<!-- 业务层 -->
	<bean id="accountService" class="lsq.spring.transaction.a_program.AccountServiceImpl">
		<!-- 注入dao -->
		<property name="accountDao" ref="accountDao"></property>
		<!-- 注入事务管理模板 -->
		<property name="transactionTemplate" ref="transactionTemplate"></property>
	</bean>
      第四步、使用TransactionTemplate进行事务管理

	@Override
	public void transfer(final String outAccount, final String inAccount, final double money) {
		//使用事务模板 管理事务
		transactionTemplate.execute(new TransactionCallbackWithoutResult() {
			@Override
			protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
				accountDao.outMoney(outAccount, money);
				int d = 1/0;
				accountDao.inMoney(inAccount, money);
			}
		});
	}
     测试代码:

package lsq.spring.transaction.a_program;

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.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class AccountTest {
	@Autowired
	@Qualifier("accountService")
	private AccountService accountService;
	
	@Test
	public void testTransfer(){
		accountService.transfer("aaa", "bbb", 200);
	}
}

       编程式事务管理流程图:


5、声明式事务管理 

      通过TransactionProxyFactoryBean对业务类创建代理,实现声明式事务管理。

      ☆:无需修改Service代码

      ☆:通过applicationContext.xml 配置为AccountService对象创建代理。

      配置事务管理器

<?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.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"/>
	
	<!-- 配置数据库连接池 -->
	<bean id="dataSource" 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.user}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean>
	
	<!-- Dao -->
	<bean id="accountDao" class="lsq.spring.transaction.b_transactionproxyfactorybean.AccountDaoImpl">
		<!-- 将连接池注入到Dao,JdbcDaoSupport会自动创建JdbcTemplate对象 -->
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- Service -->
	<bean id="accountService" class="lsq.spring.transaction.b_transactionproxyfactorybean.AccountServiceImpl">
		<!-- 注入dao -->
		<property name="accountDao" ref="accountDao"></property>
	</bean>
	
	<!-- 不管是使用编程式事务管理还是使用声明式事务管理,事务管理器都是必须的 -->
	<!-- 配置事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 为AccountService创建代理对象 -->
	<bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
		<!-- 设置目标 -->
		<property name="target" ref="accountService"></property>
		<!-- 针对接口代理 -->
		<property name="proxyInterfaces" value="lsq.spring.transaction.b_transactionproxyfactorybean.AccountService"></property>
		<!-- 增强 事务管理 -->
		<property name="transactionManager" ref="transactionManager"></property>
		<!-- 事务管理属性 -->
		<property name="transactionAttributes">
			<!-- 针对目标对象的每个方法,设置隔离级别、传播行为、是否只读 -->
			<props>
				<!-- key就是方法名 -->
				<!-- value prop格式:PROPAGATION,ISOLATION,readOnly,-Exception,+Exception -->
				<prop key="transfer">PROPAGATION_REQUIRED</prop>
			</props>
		</property>
	</bean>
	
</beans>
      使用该方式,在Test程序中,注入代理对象

	public class AccountServiceTest {
		@Autowired
		@Qualifier("accountServiceProxy") // 注入代理对象
		private AccountService accountService;
		...
	}
      事务属性设置 prop格式:PROPAGATION,ISOLATION,readOnly,-Exception,+Exception
            PROPAGATION 事务传播行为
            ISOLATION, 事务隔离级别
            readOnly  表示事务是只读的,不能进行修改操作 
            -Exception 发生这些异常事务进行回滚 (默认发生任何异常事务都会回滚)
            +Exception 事务将忽略这些异常,仍然提交事务


      ☆:基于TransactionProxyFactoryBean 创建代理对象,进行事务管理,缺点需要为 每个Bean 都创建单独代理对象,开发量巨大


6、 基于tx配置 ,为目标对象进行自动代理 (重点 :开发主流)

      在applicationContext.xml 引入 tx 和 aop 两个名词空间 

<?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.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"/>
	
	<!-- 配置数据库连接池 -->
	<bean id="dataSource" 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.user}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean>
	
	<!-- Dao -->
	<bean id="accountDao" class="lsq.spring.transaction.c_txconfig.AccountDaoImpl">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- Service -->
	<bean id="accountService" class="lsq.spring.transaction.c_txconfig.AccountServiceImpl">
		<property name="accountDao" ref="accountDao"></property>
	</bean>
	
	<!-- 配置事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 使用tx定义事务管理的增强 -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<!-- 配置方法的事务管理属性 -->
		<tx:attributes>
			<!--
				name="transfer" 事务管理方法名
				isolation="DEFAULT" 默认隔离级别
				propagation="REQUIRED"  默认传播行为
				read-only="false"  是否只读
				no-rollback-for=""  发生异常不会滚  类似+Exception
				rollback-for=""  发生异常回滚 类似-Exception
				timeout="-1"  不超时
			-->
			<tx:method name="transfer"/>
		</tx:attributes>
	</tx:advice>
	
	<!-- 使用AOP进行自动代理 -->
	<aop:config>
		<!-- 定义切点 -->
		<aop:pointcut expression="execution(* lsq.spring.transaction.c_txconfig.AccountService+.*(..))" id="mypointcut"/>
		<!-- 定义切面 -->
		<aop:advisor advice-ref="txAdvice" pointcut-ref="mypointcut"/>
	</aop:config>
	
</beans>
      ☆:定义 事务管理 tx:advice 将增强代码 应用切点上


7、 基于注解的声明式事务管理 (重点)

      第一步:在需要管理事务 类或者方法 上添加 @Transactional

      第二步:在applicationContext.xml 配置

	<!-- 配置事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 注解事务管理 -->
	<tx:annotation-driven transaction-manager="transactionManager"/>
      配置事务属性 通过@Transactional配置
       @Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, readOnly = false, noRollbackFor = ArithmeticException.class, rollbackFor =             NullPointerException.class, timeout = -1)
      * isolation 隔离级别
      * propagation 传播行为
      * readOnly 是否只读
      * noRollbackFor 发生异常不回滚
      * rollbackFor 发生异常回滚
      * timeout 超时时间 -1 不超时

小结 :

      TransactionTemplate 编程式事务管理 了解
      TransactionProxyFactoryBean 声明式事务管理 了解
      基于 tx 和 aop xml配置 声明式事务管理  重点掌握
      基于 @Transactional 注解 声明式事务管理 重点掌握 


            














Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐