Spring 事物透彻详解

一、Spring事务管理 
1、 Spring事务管理机制 三个核心组件 
1) PlatformTransactionManager  平台事务管理器  commit 提交事务、rollback 回滚事务 、 TransactionStatus getTransaction(TransactionDefinition definition) 
2) TransactionDefinition  事务定义信息(和事务管理相关一些参数)  隔离级别、传播级别、超时 、事务是否只读  (静态信息) 

3) TransactionStatus 事务状态  (事务运行过程中 一些动态信息 ) 

关系: 事务管理参数信息由 TransactionDefinition 加载, Spring事务管理由PlatformTransactionManager  完成, PlatformTransactionManager 管理事务过程中根据 TransactionDefinition 定义事务管理信息来管理事务  ,在事务运行过程中不同时刻 事务状态信息不同, TransactionStatus 就表示不同时刻事务状态信息 


第一个组件 事务管理器 PlatformTransactionManager  接口 
org.springframework.jdbc.datasource.DataSourceTransactionManager ------- 针对jdbc开发或者 iBatis开发 进行事务管理 
org.springframework.orm.hibernate3.HibernateTransactionManager  -------  针对Hibernate3开发 进行事务管理 


第二个组件 事务定义信息 TransactionDefinition 
隔离级别: 
1) read_uncommitted 读未提交   引发所有事务隔离问题(脏读、不可重复读、虚读)
2) read_committed 读已提交  不会发生脏读,会导致不可重复读和虚读 
3) repeatable_read 重复读  不会发生 脏读和不可重复读,会导致虚读 
4) serializable  序列化 采用串行方式管理事务,同一时间,只能有一个事务在操作 ,阻止所有隔离问题发生 


DEFAULT 采取数据库默认隔离级别  Oracle read_committed、 Mysql repeatable_read 

事务传播行为:(七种)
1) PROPAGATION_REQUIRED : 支持当前事务,如果不存在 就新建一个  , 将 deleteUser 和 removeAllOrders 放入同一个事务  (Spring默认提供事务传播级别)
2) PROPAGATION_SUPPORTS : 支持当前事务,如果不存在,就不使用事务  
3) PROPAGATION_MANDATORY : 支持当前事务,如果不存在,抛出异常 
4) PROPAGATION_REQUIRES_NEW : 如果有事务存在,挂起当前事务,创建一个新的事务  , deleteUser和removeAllOrder 两个事务执行,无任何关系 
5) PROPAGATION_NOT_SUPPORTED : 以非事务方式运行,如果有事务存在,挂起当前事务
6) PROPAGATION_NEVER  :  以非事务方式运行,如果有事务存在,抛出异常
7) PROPAGATION_NESTED : 如果当前事务存在,则嵌套事务执行


事物传播行为的需要性:


二、 Spring事务管理案例  转账案例(转出金额和转入金额)
Spring事务管理 分为编程式事务管理和 声明式事务管理 
编程式事务管理:在程序中通过编码来管理事务,事务代码侵入,开发中极少应用
声明式事务管理:无需侵入事务管理代码,开发维护及其方便,底层应用Spring AOP来完成 



1、编程式 完成事务管理


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');


导入jar包 spring基本6个 aop4个 jdbc2个 c3p01个 驱动1个 

导入jdbc.properties和log4j.properties

jdbc.driverClass= com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///springtx
jdbc.user= root
jdbc.password=abc
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

### direct messages to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=c:/mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

### set log levels - for more verbose logging change 'info' to 'debug' ###

log4j.rootLogger=info, stdout

业务层 IAccountServive  AccountServiceImpl 

package service;

/**
 * 以字母I开始 通常表示一个接口
 * @author 
 *
 */
public interface IAccountService {
	/**
	 * 转账操作
	 * @param outAccount  转出账户
	 * @param inAccount  转入账户
	 * @param money 转账金额 
	 */
	public void transfer(String outAccount, String inAccount , double money);
}
package service;

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

import dao.IAccountDAO;
/**
 * 编程式事务管理
 * @author 
 *
 */
public class AccountServiceImpl implements IAccountService {
	private IAccountDAO accountDAO;
	// 事务模板。编程式事务,是通过该模板进行管理
	private 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.out(outAccount, money);
//				int d= 1/0;
				accountDAO.in(inAccount, money);
				System.out.println("转账成功!");
			}
		});
		
	}

	public void setAccountDAO(IAccountDAO accountDAO) {
		this.accountDAO = accountDAO;
	}

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

DAO层 IAccountDAO  AccountDAOImpl

package dao;

/**
 * Account 操作
 * @author 
 *
 */
public interface IAccountDAO {
	// 转出
	public void out(String outAccount, double money);
	// 转入
	public void in(String inAccount, double money);
}
package dao;

import java.sql.ResultSet;
import java.sql.SQLException;

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

import domain.Account;

public class AccountDAOImpl extends JdbcDaoSupport implements IAccountDAO {

	@Override
	public void out(String outAccount, double money) {
		// 判断金额是否满足
		Account account = this.getJdbcTemplate().queryForObject("select * from account where name = ?" , new RowMapper<Account>(){
			@Override
			public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
				Account account = new Account();
				account.setId(rs.getInt("id"));
				account.setName(rs.getString("name"));
				account.setMoney(rs.getDouble("money"));
				return account;
			}
		},outAccount);
		if(account.getMoney() < money){ // 转账金额大于剩余金额
			throw new RuntimeException("余额不足无法转账!");
		}
		// 转账
		this.getJdbcTemplate().update("update account set money=money-? where name=?", money ,outAccount);
	}

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

}
在业务层中事务 TransactionTemplate 模板工具类,进行事务管理 
* 将TransactionTemplate对象注入到Service中 
* 将对应事务管理器 注入个 TransactionTemplate  
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"
       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">
	<context:property-placeholder location="classpath:jdbc.properties"/>

	<!-- 配置数据源 -->
	<!-- c3p0数据源  -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${jdbc.driverClass}"></property>
		<property name="jdbcUrl" value="${jdbc.url}"></property>
		<property name="user" value="${jdbc.user}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean>			
	
	<!-- 配置JdbcTemplate , 将数据源注入到jdbcTemplate -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 将jdbcTemplate 注入DAO -->
	<bean id="accountDAO" class="dao.AccountDAOImpl">
		<property name="jdbcTemplate" ref="jdbcTemplate"></property>
	</bean>
	
	<!-- 编程式事务管理 -->
	<!-- 1、配置事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	<!-- 2、将事务管理器 注入给 事务模板 -->
	<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
		<property name="transactionManager" ref="transactionManager"></property>
	</bean>
	<!-- 3、将模板 给Service -->
	<!-- 将DAO注入Service -->
	<bean id="accountService" class="service.AccountServiceImpl">
		<property name="accountDAO" ref="accountDAO"></property>
		<property name="transactionTemplate" ref="transactionTemplate"></property>
	</bean>
</beans>
2、 声明式事务管理 (原始方式)
TransactionProxyFactoryBean  事务代理工厂Bean为目标类生成代理,进行事务管理 

上面的其他部分不变,只需要改动AccountServiceImpl,创建新的AccountServiceImpl2

package service;

import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import dao.IAccountDAO;

/**
 * 声明式事务管理
 * 
 * @author seawind
 * 
 */
@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.DEFAULT,readOnly=false,rollbackFor=ArithmeticException.class) // 进行事务管理
public class AccountServiceImpl2 implements IAccountService {
	private IAccountDAO accountDAO;

	@Override
	public void transfer(final String outAccount, final String inAccount,
			final double money) {
		// 转账 分为转入和转出
		accountDAO.out(outAccount, money);
		//int d= 1/0;
		accountDAO.in(inAccount, money);
		System.out.println("转账成功!");

	}

	public void setAccountDAO(IAccountDAO accountDAO) {
		this.accountDAO = accountDAO;
	}

}
applicationContext2.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"
       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">
	<context:property-placeholder location="classpath:jdbc.properties"/>

	<!-- 配置数据源 -->
	<!-- c3p0数据源  -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${jdbc.driverClass}"></property>
		<property name="jdbcUrl" value="${jdbc.url}"></property>
		<property name="user" value="${jdbc.user}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean>			
	
	<!-- 配置JdbcTemplate , 将数据源注入到jdbcTemplate -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 将jdbcTemplate 注入DAO -->
	<bean id="accountDAO" class="dao.AccountDAOImpl">
		<property name="jdbcTemplate" ref="jdbcTemplate"></property>
	</bean>
	
	<!-- 将DAO注入Service -->
	<bean id="accountService" class="service.AccountServiceImpl2">
		<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="transactionManager" ref="transactionManager"></property>
		<!-- 被代理目标对象 -->
		<property name="target" ref="accountService"></property>
		<!-- 对目标业务接口生成代理 -->
		<property name="proxyInterfaces" value="service.IAccountService"></property>
		<!-- 事务定义参数 -->
		<property name="transactionAttributes">
			<!-- 为事务管理方法 配置 隔离级别、传播级别、只读  -->
			<props>
				<!-- 代理方法名 -->
				<prop key="*">PROPAGATION_REQUIRED,+java.lang.ArithmeticException</prop>
			</props>
		</property>
	</bean>
	
</beans>
transactionAttributes 事务属性配置格式 
prop格式:PROPAGATION,ISOLATION,readOnly,-Exception,+Exception
1) PROPAGATION 传播行为
2) ISOLATION 隔离级别 
3) readOnly : 只读事务,不能进行修改操作
4) -Exception : 发生这些异常回滚事务 
5) +Exception : 发生这些异常仍然提交事务 
<prop key="*">PROPAGATION_REQUIRED,+java.lang.ArithmeticException</prop>  发生java.lang.ArithmeticException 事务也会提交
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop> 事务是只读,不能进行修改操作  


3、 声明式事务管理 (使用XML配置声明式事务 基于tx/aop)
导入tx 和 aop schema 名称空间 

applicationContext3.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.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"/>

	<!-- 配置数据源 -->
	<!-- c3p0数据源  -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${jdbc.driverClass}"></property>
		<property name="jdbcUrl" value="${jdbc.url}"></property>
		<property name="user" value="${jdbc.user}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean>			
	
	<!-- 配置JdbcTemplate , 将数据源注入到jdbcTemplate -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 将jdbcTemplate 注入DAO -->
	<bean id="accountDAO" class="dao.AccountDAOImpl">
		<property name="jdbcTemplate" ref="jdbcTemplate"></property>
	</bean>
	
	<!-- 将DAO注入Service -->
	<bean id="accountService" class="service.AccountServiceImpl2">
		<property name="accountDAO" ref="accountDAO"></property>
	</bean>
	
	<!-- 事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 通过tx:advice 配置事务管理增强  -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<!-- 配置事务属性  -->
		<tx:attributes>
			<!-- 对哪个方法用事务管理 -->
			<tx:method name="transfer" propagation="REQUIRED" isolation="DEFAULT" read-only="false" />
		</tx:attributes>
	</tx:advice>
	
	<!-- 配置aop -->
	<aop:config proxy-target-class="false">
		<!-- 配置切点 -->
		<aop:pointcut expression="execution(* service.AccountServiceImpl2.*(..))" id="mytransactionpointcut"/>
		<!-- 将一个Advice作为 一个切面 Advisor -->
		<!-- 对  mytransactionpointcut 切点 进行 txAdvice 增强 -->
		<aop:advisor advice-ref="txAdvice" pointcut-ref="mytransactionpointcut"/>
	</aop:config>
	
</beans>

4、声明式事务管理 (使用注解配置 )
对需要管理事务的方法,添加注解@Transactionnal 
* @Transactionnal 可以加载类上面 或者 方法名上面 


在applicationContext.xml中添加 <tx:annotation-driven transaction-manager="transactionManager"/> 


@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.DEFAULT,readOnly=false,rollbackFor=ArithmeticException.class)
propagation=Propagation.REQUIRED 传播行为
isolation=Isolation.DEFAULT 隔离级别
readOnly=false 是否只读
rollbackFor=ArithmeticException.class 发生异常回滚 
noRollbackFor=xxx.class 发生异常 仍然提交事务 

applicationContext4.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.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"/>

	<!-- 配置数据源 -->
	<!-- c3p0数据源  -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${jdbc.driverClass}"></property>
		<property name="jdbcUrl" value="${jdbc.url}"></property>
		<property name="user" value="${jdbc.user}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean>			
	
	<!-- 配置JdbcTemplate , 将数据源注入到jdbcTemplate -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 将jdbcTemplate 注入DAO -->
	<bean id="accountDAO" class="dao.AccountDAOImpl">
		<property name="jdbcTemplate" ref="jdbcTemplate"></property>
	</bean>
	
	<!-- 将DAO注入Service -->
	<bean id="accountService" class="service.AccountServiceImpl2">
		<property name="accountDAO" ref="accountDAO"></property>
	</bean>
	
	<!-- 事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 注解驱动事务管理 -->
	<tx:annotation-driven transaction-manager="transactionManager"/>
	
</beans>
创建测试类

package test;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import service.IAccountService;

public class AccountTest {
	@Test
	// 测试用例   声明式事务管理 (使用注解配置 )
	public void demo4(){
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext4.xml");
		IAccountService accountService = (IAccountService) applicationContext.getBean("accountService");
		accountService.transfer("aaa", "bbb", 200);
	}
	
	@Test
	// 测试用例 声明式事务管理 (使用XML配置声明式事务 基于tx/aop)
	public void demo3(){
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext3.xml");
		IAccountService accountService = (IAccountService) applicationContext.getBean("accountService");
		accountService.transfer("aaa", "bbb", 200);
	}
	
	@Test
	// 测试用例  声明式事务管理 (原始方式)
	public void demo2(){
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext2.xml");
		// 原始方式进行声明事务管理,必须获得代理对象
		IAccountService accountService = (IAccountService) applicationContext.getBean("accountServiceProxy");
		accountService.transfer("aaa", "bbb", 200);
	}
	
	@Test
	// 测试用例  编程式
	public void demo1(){
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
		IAccountService accountService = (IAccountService) applicationContext.getBean("accountService");
		accountService.transfer("aaa", "bbb", 200);
	}
}
**************************************** 注解进行事务管理最方便, 也需掌握 tx/aop事务管理

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值