Spring事务管理高层抽象主要包括3个接口:
PlatformTransactionManager 事务管理器
TransactionDefinition 事务定义信息(隔离、传播、超时、只读)
TransactionStatus 事务具体运行状态
1.PlatformTransactionManager 接口 (平台相关事务管理器 ),由这个接口提供事务管理 API
方法:
commit 提交事务
rollback 回顾事务
TransactionStatus getTransaction(TransactionDefinition) 获取当时时刻事务状态
不同持久层技术,事务管理代码是不同
JDBC 通过Connection 接口 提供 的setAutoCommit(true|false) 来管理事务,setAutoCommit(false) ; 关闭自动提交,开启事务
Hibernate通过Session 接口提供 beginTransaction()的方法来开启事务
JDBC事务管理 : Spring 提供 DataSourceTransactionManager
Hibernate 事务管理: Spring 提供 HibernateTransactionManager
2.TransactionDefinition 接口 (事务定义参数 )
提供了大量常量信息 ISOLATION_* 事务隔离级别 PROPAGATION_* 事务传播行为 TIMEOUT_DEFAULT 事务超时时间
用户可以通过Spring配置文件,指定事务 隔离级别、传播行为、超时时间、 是否只读等
隔离级别:
隔离级别 来源于 事务隔离性引发并发问题 (脏读、 不可重复读、 幻读)
脏读:一个事务读取了另一个事务改写但还未提交的数据,如果这些数据被回滚,则读到的数据是无效的。
不可重复读:在同一事务中,多次读取同一数据返回的结果有所不同。换句话说就是,后续读取可以读到另一事务已提交的更新数据。相反,“可重复读”在同一事务中多次读取数据时,能够保证所读数据一样,也就是,后续读取不能读到另一事务已提交的更新数据。
幻读:一个事务读取了几行记录后,另一个事务插入一些记录,幻读就发生了。再后来的查询中,第一个事务就会发现有些原来没有的记录。
隔离级别,是数据库内部一个机制, JDBC 和 Spring 事务管理,都提供对应支持
事务传播行为 :
事务传播行为,并不是数据库内部支持特性,而是Spring 为了解决企业开发中实际事务管理问题,提供了 七种 事务的传播行为
REQUIRED 、SUPPORTS 、MANDATORY 三个行为,都是支持当前事务,被调用方法和原来方法可以处于同一个事务中 (例如: 删除客户、删除订单 )
REQUIRES_NEW、NOT_SUPPORT 、NEVER 三个行为,都是不支持当前事务,被调用方法和原来方法 一定不会处于同一个事务 (例如: ATM取钱 、打印凭条 )
NESTED 是最特殊的一种 ,嵌套事务执行 ,原理就是SavePoint 回滚点技术
—- 嵌套事务,仍然使用是同一个事务 ,可以在事务执行过程中 设置回滚点,如果被调用方法出错后,可以选择将程序 回滚到 回滚点 (只对DataSourceTransactionManager有效 )
重点区别: REQUIRED、 REQUIRES_NEW 、NESTED
REQUIRED : 两个操作,处于同一个事务中,要么都成功,要么都失败
REQUIRES_NEW : 两个操作 分别处于两个不同事务,彼此之间不会互相影响
NESTED : 两个操作,处于同一个事务,但是内部采用 SavePoint机制,在一个操作出现问题时,回滚到保存点,继续操作
3.TransactionStatus 接口 (事务状态 )
每个时刻 事务的状态是不同的 , 该对象数据可以表示某个时刻的事务状态信息
3个接口之间的的关系: PlatformTransactionManager 是真正用来进行事务管理对象, TransactionDefinition是用户事务定义信息, PlatformTransactionManager根据 TransactionDefinition 信息来进行事务管理, 在管理事务过程中,每个时间点都可以获取事务状态 (TransactionStatus )
Spring 事务管理,
Spring的事务管理支持编程式事务管理 (通过代码进行事务管理) 和 声明式事务管理 (通过配置进行事务管理,不需要编写格外的代码 ),推荐使用 声明式事务管理, 声明式事务管理底层实现 就是 AOP 编程。
代码示例:
数据库准备
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');
AccountService.java
package com.my.service;
public interface AccountService {
public void transfer(String outAccount, String inAccount, double money);
}
AccountServiceImpl.java
package com.my.service;
import com.my.dao.AccountDao;
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(String outAccount, String inAccount, double money) {
accountDao.out(outAccount, money);
//int d = 1/0;
accountDao.in(inAccount, money);
}
}
AccountDao.java
package com.my.dao;
public interface AccountDao {
public void out(String outAccount, double money);
public void in(String inAccount , double money);
}
AccountDaoImpl.java
package com.my.dao;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
@Override
public void out(String outAccount, double money) {
String sql = "update account set money = money - ? where name = ?";
this.getJdbcTemplate().update(sql, money, outAccount);
}
@Override
public void in(String inAccount, double money) {
String sql = "update account set money = money + ? where name = ?";
this.getJdbcTemplate().update(sql, money, inAccount);
}
}
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.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">
<!-- 引入外部properties 属性文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- C3P0连接池 -->
<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.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 将连接池交给JdbcTemplate 构造模板对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="accountDao" class="com.my.dao.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<!-- 目标 -->
<bean id="accountService" class="com.my.service.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 增强 -->
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 就是事务管理增强
isolation 隔离级别
no-rollback-for 发生哪些异常,事务不进行回滚
propagation 传播行为
read-only 事务是否只读
rollback-for 发生哪些异常,事务回滚
timeout 超时时间 -1 使用数据库内部默认超时时间
read-only 只读 和 no-rollback-for 发生异常事务不回滚
-->
<tx:advice id="mytxAdvice" transaction-manager="transactionManager">
<!-- 事务管理属性配置 -->
<tx:attributes>
<!-- 对哪些方法 ,进行事务事务配置 -->
<tx:method name="transfer" /> <!-- 使用默认事务管理属性 -->
<tx:method name="get*" read-only="true"/> <!-- 将查询的方法设置为 只读 -->
</tx:attributes>
</tx:advice>
<!-- 配置advisor -->
<aop:config>
<!-- 定义切点 -->
<aop:pointcut expression="execution(* com.my.service.AccountServiceImpl.*(..))" id="mypointcut"/>
<!-- 定义切面 -->
<aop:advisor advice-ref="mytxAdvice" pointcut-ref="mypointcut"/>
</aop:config>
</beans>
使用注解实现 声明式事务管理
第一步 : 在需要管理事务 方法上 ,添加 @Transactional
该注解可以用于类或者方法上,如果用于类上,对对象中所有方法开启事务管理 ,[email protected] 注解中配置事务的属性
@Transactional
public class AccountServiceImpl implements IAccountService{
第二步 : 在配置文件中 ,配置使用注解进行声明式事务管理
<!-- 使用注解进行声明式事务管理 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
原创文章,作者:Maggie-Hunter,如若转载,请注明出处:https://blog.ytso.com/12093.html