【08】Spring 事务处理
文章目录
1. 事务
1.1 事务的概念
事务是我们对数据库操作的最基本的单元,事务一般指的是一组操作,要么都成功,有一个失败,一组操作都失败。
1.2 事务特性
原子性 : 强调事务的不可分割。
一致性 : 事务的执行的前后数据的完整性保持一致。
隔离性 : 一个事务执行的过程中,不应该受到其他事务的干扰。
持久性 : 事务一旦结束,数据就持久到数据库。
1.3 不考虑隔离性引发安全性问题
- 脏读 : 一个事务读到了另一个事务的未提交的数据
- 不可重复读: 一个事务读到了另一个事务已经提交的 update 的数据导致多次查询结果不一致.
- 幻读 : 一个事务读到了另一个事务已经提交的 insert 的数据导致多次查询结果不一致.
幻读和不可重复读是两个容易混淆的概念,幻读是指读到了其他已经提交事务的新增数据,而不可重复读是指读到了已经提交事务的更改数据(更改或删除),为了避免这两种情况,采取的对策是不同的,防止读取到更改数据,只需要对操作的数据添加行级锁,阻止操作中的数据发生变化,而防止读取到新增数据,则往往需要添加表级锁——将整个表锁定,防止新增数据(Oracle使用多版本数据的方式实现)。
1.4 解决读问题:设置事务隔离级别
针对读取数据时可能产生的不一致现象,在SQL92标准中定义了4个事务的隔离级别:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read uncommitted(读未提交) | 是 | 是 | 是 |
Read committed(读已提交) | 否 | 是 | 是 |
Repeatable read(可重复读) | 否 | 否 | 是 |
Serializable(串行读) | 否 | 否 | 否 |
- Oracle默认的隔离级别是 Read committed。
- MySQL默认的隔离级别是 Repeatable Read。
2. Spring 事务管理
2.1 Spring事务管理的两种方式
- 编程式事务:将事务控制代码编写在具体的业务逻辑方法实现中,不推荐使用。
- 声明式事务:声明式其实就是不写代码来实现(xml、注解)。
2.2 Spring事务管理API
- Spring事务管理高层抽象主要包括3个接口
① PlatformTransactionManager 事务管理器
② TransactionDefinition(事务定义信息【隔离、传播、超时、只读】)
③ TransactionStatus 事务运行时状态
-
PlatformTransactionManager:平台事务管理器
– 使用 Spring JDBC 或 iBatis 进行持久化数据时使用org.springframework.jdbc.datasource.DataSourceTransactionManager
– 使用 Hibernate 版本进行持久化数据时使用
org.springframework.orm.hibernate3.HibernateTransactionManager
-
TransactionDefinition:事务定义信息
隔离级别
传播行为
超时信息
是否只读 -
TransactionStatus:事务的状态
– 记录事务的状态
平台事务管理根据事务定义的信息进行事务的管理,事务管理的过程中产生一些状态,将这些状态记
录到 TransactionStatus 里面
2.3 Spring事务管理 XML 配置
– 转账业务环境搭建
-
数据表
CREATE TABLE t_bank ( id INT PRIMARY KEY, NAME VARCHAR(50) NOT NULL, balance DECIMAL(10,4) ) -- 初始数据 INSERT INTO t_bank(id,NAME,balance) VALUES (1,'张三',3000); INSERT INTO t_bank(id,NAME,balance) VALUES (2,'李四',0);
-
触发器
DELIMITER $$ CREATE /*[DEFINER = { user | CURRENT_USER }]*/ TRIGGER `demo`.`tri_bank` AFTER UPDATE ON `demo`.`t_bank` FOR EACH ROW BEGIN DECLARE msg VARCHAR(32); IF new.`balance` < 0 THEN SET msg = "Error : 余额不能为负! "; /*抛出异常*/ SIGNAL SQLSTATE 'HY000' SET MESSAGE_TEXT = msg; END IF; END$$ DELIMITER ;`
-
dao
import com.hxzy.dao.BankDao; public class BankDaoImpl implements BankDao { @Override public void outMoney(int from, double money) { System.out.println("取钱 -- 取款人:" + from + " , 取款金额: " + money); jdbcTemplate.update("update t_Bank set balance = balance - ? where id = ?", money, from); } @Override public void inMoney(int to, double money) { System.out.println("存钱 -- 收款人:" + to + " , 收款金额: " + money); jdbcTemplate.update("update t_Bank set balance = balance + ? where id = ?", money, to); } }
-
service
public class BankServiceImpl implements BankService { private BankDao bankDao; public void setBankDao(BankDao bankDao) { this.bankDao = bankDao; } @Override public void transfer(int from, int to, double money) { //取钱 bankDao.outMoney(from, money); //存钱 bankDao.inMoney(to, money); } }
-
applicationContext.xml
– spring容器环境中添加 tx 命名空间<?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:p="http://www.springframework.org/schema/p" 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-4.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd"> <!-- 配置数据源:spring的内置连接池 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url"> <value><![CDATA[jdbc:mysql://127.0.0.1:3306/demo?useUnicode=true&characterEncoding=utf-8]]></value> </property> <property name="username" value="root"/> <property name="password" value="123456"/> </bean> <!-- 配置 Spring 的模板文件 --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 配置BankService --> <bean id="bankService" class="com.hxzy.service.impl.BankServiceImpl"> <property name="bankDao" ref="bankDao"/> </bean> <!-- 配置BankDao --> <bean id="bankDao" class="com.hxzy.dao.impl.BankDaoImpl"> <property name="jdbcTemplate" ref="jdbcTemplate"/> </bean> </beans>
-
配置事务管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
-
配置事务增强
<tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="transfer" propagation="REQUIRED"/> </tx:attributes> </tx:advice>
-
配置切面
<aop:config> <!-- 切入点 --> <aop:pointcut expression="execution( * com.hxzy.service..*.*(..))" id="pointcut"/> <!-- 切面 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/> </aop:config>
-
Test
public class TestClass06 { @Test public void testStudy() { ApplicationContext context = new ClassPathXmlApplicationContext("spring/applicationContext-bank.xml"); BankService bankService = context.getBean(BankService.class); bankService.transfer(1, 2, 6000); } }
2.4 Spring事务管理 注解 配置
-
配置事务管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
-
开启事务管理的注解
<tx:annotation-driven transaction-manager="transactionManager"/>
-
在使用事务的类上添加事务注解
@Transactional(readOnly = true) public class BankServiceImpl implements BankService { private BankDao bankDao; public void setBankDao(BankDao bankDao) { this.bankDao = bankDao; } @Transactional(readOnly = false) @Override public void transfer(int from, int to, double money) { //取钱 bankDao.outMoney(from, money); //存钱 bankDao.inMoney(to, money); } }
-
Test
public class TestClass06 { @Test public void testStudy() { ApplicationContext context = new ClassPathXmlApplicationContext("spring/applicationContext-bank.xml"); BankService bankService = context.getBean(BankService.class); bankService.transfer(1, 2, 6000); } }