【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事务管理的两种方式

  1. 编程式事务:将事务控制代码编写在具体的业务逻辑方法实现中,不推荐使用。
  2. 声明式事务:声明式其实就是不写代码来实现(xml、注解)。

2.2 Spring事务管理API

  • Spring事务管理高层抽象主要包括3个接口
    ① PlatformTransactionManager 事务管理器
    ② TransactionDefinition(事务定义信息【隔离、传播、超时、只读】)
    ③ TransactionStatus 事务运行时状态
  1. PlatformTransactionManager:平台事务管理器
    – 使用 Spring JDBC 或 iBatis 进行持久化数据时使用

    org.springframework.jdbc.datasource.DataSourceTransactionManager

    – 使用 Hibernate 版本进行持久化数据时使用

    org.springframework.orm.hibernate3.HibernateTransactionManager

  2. TransactionDefinition:事务定义信息

    隔离级别
    传播行为
    超时信息
    是否只读

  3. TransactionStatus:事务的状态
    – 记录事务的状态

平台事务管理根据事务定义的信息进行事务的管理,事务管理的过程中产生一些状态,将这些状态记
录到 TransactionStatus 里面

2.3 Spring事务管理 XML 配置

– 转账业务环境搭建

  1. 数据表

     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);
    
  2. 触发器

     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 ;`
    
  3. 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);
     	}
     }
    
  4. 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);
     	}
     }
    
  5. 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>
    
  6. 配置事务管理器

     	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
     		<property name="dataSource" ref="dataSource"/>
     	</bean>
    
  7. 配置事务增强

        <tx:advice id="txAdvice" transaction-manager="transactionManager">
     		<tx:attributes>
     			<tx:method name="transfer" propagation="REQUIRED"/>
     		</tx:attributes>
     	</tx:advice>
    
  8. 配置切面

     	<aop:config>
     		<!-- 切入点 -->
     		<aop:pointcut expression="execution( * com.hxzy.service..*.*(..))" id="pointcut"/>
     		<!-- 切面 -->
     		<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
     	</aop:config>
    
  9. 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事务管理 注解 配置

  1. 配置事务管理器

     	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
     		<property name="dataSource" ref="dataSource"/>
     	</bean>
    
  2. 开启事务管理的注解

        <tx:annotation-driven transaction-manager="transactionManager"/>
    
  3. 在使用事务的类上添加事务注解

     	@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);
     		}
     	}
    
  4. 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);
     		}   	
     	}    
    

猜你喜欢

转载自blog.csdn.net/Spectre_win/article/details/89846291