Spring事务管理

x33g5p2x  于2021-03-14 发布在 其他  
字(11.5k)|赞(0)|评价(0)|浏览(317)

事务:事务指的是逻辑上的一组操作,这组操作要么全部成功,要么全部失败
事务的特性:
原子性:指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性:指事务前后数据的完整性必须保持一致。
隔离性:指多个用户并发访问数据库时,一个用户的事务不能被其它用户事务所干扰,多个并发事务之间的数据要互相隔离。
如果没有隔离线,会引发脏读、不可重复读、幻读等安全问题。
持久性:指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,即使数据库发生故障也不应该对其造成影响。
Spring事务管理
Spring 事务管理高层抽象主要3个接口:
1.事务管理器(提交、回滚等)
PlatformTransactionManager
Spirng为不同的持久化框架提供不同PlatformTransactionManager接口实现

2.事务定义信息(隔离、传播、超时、只读)
TransactionDefinition
隔离级别以及作用

事务的传播行为(主要处理业务层方法之间的相互调用产生的这种事务应该如何进行传递的复杂问题)

eg:有两个操作aa和bb
PROPAGATION_REQUIRED
是保证aa和bb操作是在一个事务中进行
PROPAGATION_REQUIRES_NEW
是保证aa和bb操作不在一个事务之中的
PROPAGATION_NESTED
是一个嵌套事务,保证aa执行完以后设置一个保存点,如果bb发生异常之后我们可以让它回滚到保存点的位置或者回滚到最初值的状态
3.事务具体运行状态(是否取消、是否有保存点、是不是新事务)
TransactionStatus

Spring支持两种方式事务管理
--编程式事务管理
在实际应用中很少用
通过TransactionTemplate手动管理事务
--使用XML配置声明式事务
开发中推荐(代码入侵性最小)
Spring的声明式事务通过AOP实现的

以转账案例实现Spring事务管理
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');
INSERT INTO `account` VALUES ('3', 'ccc', '1000');

所需要的配置文件以及包
密码:jfbu
2.编写applicationContext.xml文件

<!-- 引入外部属性文件 -->
	<context:property-placeholder location="classpath:jdbc.properties"/>
	
	<!-- 配置c3p0连接池-->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${jdbc.driverClass}"/>
		<property name="jdbcUrl" value="${jdbc.url}"/>
		<property name="user" value="${jdbc.username}"/>
		<property name="password" value="${jdbc.password}"/>
	</bean>
	
	<!-- 配置业务层 -->
	<bean id="accountServic" class="com.spring.demo1.AccountServiceImpl">
	
	</bean>
	
	<!-- 配置DAO层 -->
	<bean id="accountDao" class="com.spring.demo1.AccountDaoImpl">
		
	</bean>

4.DAO层
AccountDao接口

public interface AccountDao {
	
	public void outMoney(String out ,Double money);
	
	public void inMoney(String in,Double money);
}

AccountDaoImpl类
首先要在applicationContext.xml配置文件中配置数据池的注入

<!-- 配置DAO层 -->
	<bean id="accountDao" class="com.spring.demo1.AccountDaoImpl">
		<!-- 注入jdbc数据池模版,使得可以在AccountDaoImpl直接使用模版 -->
		<property name="dataSource" ref="dataSource"/>
	</bean>

其次在AccountDaoImpl类中写sql语句以及获取模版传入参数

public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {

	@Override
	public void outMoney(String out, Double money) {
		// TODO Auto-generated method stub
		String sql = "update account set money = money - ? where name = ?";
		//获取模版传入参数
		this.getJdbcTemplate().update(sql, money, out);
	}

	@Override
	public void inMoney(String in, Double money) {
		// TODO Auto-generated method stub
		String sql = "update account set money = money + ? where name = ?";
		this.getJdbcTemplate().update(sql,money,in);
	}

}

5.Service层
AccountService接口

public interface AccountService {
	/**
	 * 
	 * @param out  转出账号
	 * @param in   转入账号
	 * @param money  转账金额
	 */
	public void transfer(String out,String in,Double money);
}

AccountServiceImpl类,首先注入AccountDao的类,先要提供set方法

public class AccountServiceImpl类,首先注入 implements AccountService {
	//注入DAO的类
	private AccountDao accountDao;
	
	public void setAccountDao(AccountDao accountDao) {
		this.accountDao = accountDao;
	}
	@Override
	public void transfer(String out, String in, Double money) {
		// TODO Auto-generated method stub
		
	}

}

其次要在applicationContext.xml配置文件中配置

<!-- 配置业务层 -->
	<bean id="accountServic" class="com.spring.demo1.AccountServicImpl">
		<property name="accountDao" ref="accountDao"/>
	</bean>

最后再在transfer方法中调用AccountDaoImpl中的inMoney以及outMoney方法

@Override
	public void transfer(String out, String in, Double money) {
		// TODO Auto-generated method stub
		accountDao.outMoney(out, money);
		accountDao.inMoney(in, money);
	}

测试类SpringDemo1

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringDemo1 {
	
	//测试业务层类
	@Resource(name="accountServic")
	private AccountService accountService;
	
	@Test
	public void demo1(){
		accountService.transfer("aaa", "bbb", 200d);
	}
}

添加事务管理
方式一:
编程式事务管理
首先在applicationContext.xml配置文件获取管理事务的类(这里使用jdbc事务管理)

<!-- 配置事务管理 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		
	</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"></property>
	</bean>

然后回到AccountServiceImpl类中注入事务模板并且给出set方法

private TransactionTemplate transactionTemplate;

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

接着回到applicationContext.xml配置文件中在业务层注入事务管理方法

<!-- 配置业务层 -->
	<bean id="accountServic" class="com.spring.demo1.AccountServiceImpl">
		<property name="accountDao" ref="accountDao"/>
		<!-- 注入事务管理的模板 -->
		<property name="transactionTemplate" ref="transactionTemplate"/>
	</bean>

然后回到AccountServiceImpl类中更改transfer()fangfa,通过transactionTemplate的execute()方法的TransactionCallbackWithoutResult内部类实现transfer()的功能,同时要把transfer(String out, String in, Double money)的变量加上final修饰符、

	@Override
	public void transfer(final String out, final String in, final Double money) {
		transactionTemplate.execute(new TransactionCallbackWithoutResult() {
			
			@Override
			protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
				accountDao.outMoney(out, money);
				accountDao.inMoney(in, money);
				
			}
		});
	}

方式二:
声明式事务管理
(1)基于TransactionProxyFactoryBean的方式(很少使用,因为需要为每个进行事务管理类,配置一个TransactionProxyFactoryBean进行增强)
新建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"
     xmlns:aop="http://www.springframework.org/schema/aop"
     xmlns:tx="http://www.springframework.org/schema/tx"
     xmlns:task="http://www.springframework.org/schema/task"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context-3.1.xsd
         http://www.springframework.org/schema/aop
         http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
         http://www.springframework.org/schema/tx
         http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
         http://www.springframework.org/schema/task
		 http://www.springframework.org/schema/task/spring-task-3.1.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 name="jdbcUrl" value="${jdbc.url}"/>
		<property name="user" value="${jdbc.username}"/>
		<property name="password" value="${jdbc.password}"/>
	</bean>
	
	<!-- 配置业务层 -->
	<bean id="accountServic" class="com.spring.demo2.AccountServiceImpl">
		<property name="accountDao" ref="accountDao"/>
	</bean>
	
	<!-- 配置DAO层 -->
	<bean id="accountDao" class="com.spring.demo2.AccountDaoImpl">
		<!-- 注入jdbc数据池模版,使得可以在AccountDaoImpl直接使用模版 -->
		<property name="dataSource" ref="dataSource"/>
	</bean>
</beans>

新建AccountServiceImpl类

public class AccountServiceImpl implements AccountService {
	// 注入DAO的类
	private AccountDao accountDao;

	public void setAccountDao(AccountDao accountDao) {
		this.accountDao = accountDao;
	}
	
	@Override
	public void transfer(String out, String in, Double money) {
		// TODO Auto-generated method stub
		accountDao.outMoney(out, money);
		accountDao.inMoney(in, money);
	}
}

首先回到applicationContext2.xml配置文件中配置事务管理器

	<!-- 配置事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"/>
	</bean>

通过TransactionProxyFactoryBean配置业务层的代理产生代理对象,之后使用target配置目标对象,通过transactionManager注入事务管理器,最后注入事务的属性,并配置prop的格式

	<!-- 配置业务层的代理 -->
	<bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
		<!-- 配置目标对象 -->
		<property name="target" ref="accountServic"/>
		<!-- 注入事务管理器 -->
		<property name="transactionManager" ref="transactionManager"/>
		<!-- 注入事务的属性 -->
		<property name="transactionAttributes">
			<props>
			<!-- 
				prop的格式
				*	PROPAGATION    :事务的传播行为
				*	ISOLATION	        :事务的隔离级别
				*	readOnly   	        :只读
				*	-Exception	        : 发生哪些异常回滚事务
				*	+Exception	        :发生哪些异常事务不回滚
			 -->
				<prop key="transfer">PROPAGATION_REQUIRED</prop>
			</props>
		</property>
	</bean>

测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext2.xml")
public class SpringDemo2 {
	
	/**
	 * 注入代理类
	 */
	//@Resource(name="accountServic")
	@Resource(name="accountServiceProxy")
	private AccountService accountService;
	
	@Test
	public void test(){
		accountService.transfer("bbb", "aaa", 200d);
	}
}

(2)基于AspectJ的XML方式(经常使用,一旦配置好,类不需要添加任何东西,不需要修改代码)
同上新建applicationContext3.xml配置文件并在applicationContext3.xml配置文件中配置事务管理器

	<!-- 配置事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"/>
	</bean>

其次通过tx:advice标签配置事务通知(也就是事务的增强),然后配置事务的属性(也就是需要通知的方法以及事务的传播行为、隔离等级等),然后再配置切入面以及切入点

	<!-- 配置事务的通知:(事务的增强) -->
	<tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager">
		<tx:attributes>dataSourceTransactionManager
			<tx:method name="transfer" propagation="REQUIRED"/>
		</tx:attributes>
	</tx:advice>
	
	<!--  配置切面-->
	<aop:config>
		<!-- 配置切入点 -->
		<aop:pointcut expression="execution(* com.spring.demo3.AccountService+.*(..))" id="pointcut1"/>
		<!-- 配置切面 -->
		<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>
	</aop:config>

(3)基于注解的方式(经常使用,配置简单,需要在业务层添加一个@Transactional的注解)
前两步于上面一样,创建applicationContext4.xml配置文件,并且配置事务管理器

	<!-- 配置事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"/>
	</bean>

通过tx:annotation-driven/标签开启注解事务

	<!-- 开启注解器 -->
	<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>

最后在需要开启事务管理的类进行注解(实例中的类为:AccountServiceImpl)

@Transactional
public class AccountServiceImpl implements AccountService {
	// 注入DAO的类
	private AccountDao accountDao;

	public void setAccountDao(AccountDao accountDao) {
		this.accountDao = accountDao;
	}
	
	@Override
	public void transfer(String out, String in, Double money) {
		// TODO Auto-generated method stub
		accountDao.outMoney(out, money);
		accountDao.inMoney(in, money);
	}
}
上一篇:Spring AspectJ
下一篇:Spring MVC起步

相关文章