04【Spring声明式事、传播行为、AOP事务控制】
admin
2024-02-29 07:31:20
0次

文章目录

  • 04【Spring声明式事、传播行为、AOP事务控制】
  • 一、Spring声明式事务
    • 1.1 回顾事务
      • 1.1.1 什么是事务?
      • 1.1.2 事务四大特性
      • 1.1.3 事务并发访问问题
      • 1.1.4 事务隔离级别
      • 1.1.5 MySQL事务相关命令
    • 1.2 Spring事务管理介绍
      • 1.2.1 编程式事务
      • 1.2.2 声明式事务
      • 1.2.3 Spring事务管理器
      • 1.2.4 事务传播行为
    • 1.3 Spring实现声明式事务
      • 1.3.1 案例准备
        • 1)SQL脚本:
        • 2)Maven依赖:
        • 3)jdbc.properties:
        • 4)实体类:
      • 1.3.2 事务通知XML配置
        • 1)Spring配置:
        • 2)业务类:
        • 3)测试类
      • 1.3.3 注解配置
        • 1)Spring配置
        • 2)业务类
        • 3)测试类

04【Spring声明式事、传播行为、AOP事务控制】

一、Spring声明式事务

1.1 回顾事务

1.1.1 什么是事务?

如果一个业务操作中多次访问了数据库,必须保证每条SQL语句都执行成功。如果其中有一条执行失败,所有已经执行过的代码必须回滚。回到没有执行前的状态。称为事务。要么所有的SQL语句全部执行成功,要么全部失败。

1.1.2 事务四大特性

事务特性含义
原子性(Atomicity)事务是工作的最小单元,整个工作单元要么全部执行成功,要么全部执行失败
一致性(Consistency)事务执行前与执行后,数据库中数据应该保持相同的状态。如:转账前总金额与转账后总金额相同。
隔离性(Isolation)事务与事务之间不能互相影响,必须保持隔离性。
持久性(Durability)如果事务执行成功,对数据库的操作是持久的。

1.1.3 事务并发访问问题

并发访问下事务产生的问题:

当同时有多个用户在访问同一张表中的记录,每个用户在访问的时候都是一个单独的事务。

事务在操作时的理想状态是:事务之间不应该相互影响,实际应用的时候会引发下面三种问题。应该尽量避免这些问题的发生。通过数据库本身的功能去避免,设置不同的隔离级别。

  • 脏读: 一个事务(用户)读取到了另一个事务没有提交的数据
  • 不可重复读:一个事务多次读取同一条记录,出现读取数据不一致的情况。一般因为另一个事务更新了这条记录而引发的。
  • 幻读:在一次事务中,多次读取到的条数不一致

1.1.4 事务隔离级别

级别名字隔离级别脏读不可重复读幻读数据库默认隔离级别
1读未提交read uncommitted是是是
2读已提交read committed否是是Oracle和SQL Server
3可重复读repeatable read否否是MySQL
4串行化serializable否否否
  • 1)Read uncommitted (读未提交): 简称RU隔离级别,所有事务中的并发访问问题都会发生,读取的是其他事务没有提交的数据

  • 2)Read committed (读已提交):简称RC隔离级别,会引发不可重复读和幻读的问题,读取的永远是其他事务提交的数据

  • 3)Repeatable read (可重复读):简称RR隔离级别,有可能会引发幻读的问题,一次事务读取到的同一行数据,永远是一样

  • 4)Serializable (串行化): 可以避免所有事务产生的并发访问的问题 效率及其低下

1.1.5 MySQL事务相关命令

  • 测试表:
drop table if exists account;-- 创建数据表  
CREATE TABLE account (  id INT PRIMARY KEY AUTO_INCREMENT,  name VARCHAR(10),  money DOUBLE  
);  -- 添加数据  
INSERT INTO account (name, money) VALUES ('a', 1000), ('b', 1000);
  • 查询事务隔离级别:
select @@tx_isolation;
  • 设置事务隔离级别:
set global transaction isolation level 四种隔离;set global transaction isolation level read uncommitted;
set global transaction isolation level read committed;
set global transaction isolation level repeatable read;
set global transaction isolation level serializable;

需要重启客户端会话

  • 开启事务:
start transaction;
或者
begin;
  • 提交事务:
commit;
  • 回滚事务:
rollback;

1.2 Spring事务管理介绍

1.2.1 编程式事务

  • 什么是编程式事务?

编程式事务简单的来说就是采用编程的方式来管理事务,编程式事务需要将事务管理的代码写入到业务方法中,相对于核心业务而言,事务管理的代码显然属于非核心业务,对核心业务代码的侵入性太大;而且如果多个模块都使用同样模式的代码进行事务管理,显然会造成较大程度的代码冗余。

我们之前使用的JDBC API来操作事务就是编程式事务;

1.2.2 声明式事务

  • 什么是声明式事务?

声明式事务以声明的方式来实现事务管理,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

声明式事务简单的来说就是通过一些表达式声明(拦截)一堆需要被事务管理的方法,然后被声明(拦截)的方法通过AOP的方式进行事务管理(执行目标方法之前开启事务,执行目标方法之后提交或者回滚事务)

显然声明式事务管理要优于编程式事务管理:它将事务管理代码从业务方法中分离出来,这正是Spring倡导的非侵入式的开发方式。声明式事务管理使业务代码不受干扰;一个普通的对象,只要加上注解就可以获得完全的事务支持。

声明式事务唯一不足地方是,它的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。但是即便有这样的需求,也存在很多变通的方法,比如,可以将需要进行事务管理的代码块独立为方法等等。

1.2.3 Spring事务管理器

Spring从不同的事务管理API中抽象出了一整套事务管理机制,让事务管理代码从特定的事务技术中独立出来。开发人员通过配置的方式进行事务管理,而不必了解其底层是如何实现的。

Spring所有的事务管理策略类都继承自org.springframework.transaction.PlatformTransactionManager接口。

它为事务管理封装了一组独立于技术的方法。无论使用Spring的哪种事务管理策略(编程式或声明式),事务管理器都是必须的。

  • 事务管理器主要的实现:
    • DataSourceTransactionManager:使用Spring JDBC 或 Mybatis 等基于DataSource数据源的持久化技术时,使用 该事务管理器
    • JpaTransactionManager:使用JPA时采用的事务管理器
    • JtaTransactionManager:具有多个数据源的全局事务使用该事务管理器
    • JdoTransactionManager:使用JDO进行持久化时 ,使用该事务管理器
    • HibernateTransactionManager:使用Hibernate进行持久化时,使用该事务管理器

1.2.4 事务传播行为

当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。

例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。事务的传播行为可以由传播属性指定。Spring定义了7种类传播行为,在org.springframework.transaction.TransactionDefinition类中被定义。

静态常量说明
PROPAGATION_REQUIRED如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择,也是Spring的默认值。
PROPAGATION_SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

TransactionDefinition类中不仅定义了事务的传播行为,也定义了很多事务的隔离级别:

静态常量说明
ISOLATION_DEFAULT使用数据库默认的隔离级别,Spring默认值
ISOLATION_READ_UNCOMMITTED读未提交
ISOLATION_READ_COMMITTED读已提交
ISOLATION_REPEATABLE_READ可重复读
ISOLATION_SERIALIZABLE串行化

1.3 Spring实现声明式事务

1.3.1 案例准备

1)SQL脚本:

drop database if exists spring;create database spring;use spring;-- 创建数据表  
CREATE TABLE account (  id INT PRIMARY KEY AUTO_INCREMENT,  name VARCHAR(10),  money DOUBLE  
);  -- 添加数据  
INSERT INTO account (name, money) VALUES ('a', 1000), ('b', 1000);

2)Maven依赖:


4.0.0com.dfbz01_Spring1.0-SNAPSHOTorg.springframeworkspring-context5.2.9.RELEASEorg.springframeworkspring-jdbc5.2.9.RELEASEorg.springframeworkspring-tx5.2.9.RELEASEorg.springframeworkspring-aspects5.2.9.RELEASEorg.springframeworkspring-test5.2.9.RELEASEmysqlmysql-connector-java5.1.48com.alibabadruid1.2.1junitjunit4.12testorg.projectlomboklombok1.18.18

3)jdbc.properties:

jdbc.username=root
jdbc.password=admin
jdbc.url=jdbc:mysql:///spring
jdbc.driverClassName=com.mysql.jdbc.Driver

4)实体类:

package com.dfbz.entity;import lombok.Data;/*** @author lscl* @version 1.0* @intro:*/
@Data
public class Account {private Integer id;private String name;private Double money;
}

1.3.2 事务通知XML配置

1)Spring配置:















**默认情况下,任何的运行时异常事务都会回滚,但编译时异常都不会进行回滚;**我们可以通过一些配置来调整

  • no-rollback-for:指定什么异常不回滚,可以写多个,以逗号隔开;

  • rollback-for:该配置针对于编译时异常;可以写多个,以逗号隔开;

上述两个参数在指定异常时,如果指定某个异常的父类,包括这个异常的所有子类异常都会回滚;

如:no-rollback-for指定的参数是Exception则代表Java中的任何异常都不回滚,

rollback-for指定的参数是Exception则Java中任何的异常都回滚;

2)业务类:

package com.dfbz.service;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;/*** @author lscl* @version 1.0* @intro:*/
@Service
public class AccountService {@Autowiredprivate JdbcTemplate jdbcTemplate;/*** @param str     : 谁要转账* @param target: 转账给谁* @param flag:   模拟异常 true:出现异常 false:不出现异常*/public void transfer(String str, String target, Boolean flag) {jdbcTemplate.update("update account set money=money-500 where name=?", str);if (flag) {// 模拟异常int i = 1 / 0;}jdbcTemplate.update("update account set money=money+500 where name=?", target);}/**** @param id*/public void findById(Integer id) {Account account = jdbcTemplate.queryForObject("select * from account where id=?",new BeanPropertyRowMapper<>(Account.class),id);}
}

3)测试类

package com.dfbz.test;import com.dfbz.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;/*** @author lscl* @version 1.0* @intro:*/
@RunWith(SpringRunner.class)
@ContextConfiguration(locations = "classpath:spring.xml")
public class Demo01 {@Autowiredprivate AccountService accountService;@Testpublic void test1(){accountService.transfer("a","b",false);}
}

1.3.3 注解配置

1)Spring配置



2)业务类

package com.dfbz.service;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;/*** @author lscl* @version 1.0* @intro:*/
@Service
public class AccountService {@Autowiredprivate JdbcTemplate jdbcTemplate;/*** @param str     : 谁要转账* @param target: 转账给谁* @param flag:   模拟异常 true:出现异常 false:不出现异常*/
/*    @Transactional(transactionManager = "transactionManager",              // 事务管理器的名称propagation = Propagation.REQUIRED,                     // 传播行为isolation = Isolation.DEFAULT,                          // 隔离级别timeout = 3000,                                         // 超时时间readOnly = false,                                       // 是否只读rollbackFor = {MyException.class},       				// 出现该异常回滚noRollbackFor = {ArithmeticException.class}             // 出现指定异常不回滚)    */@Transactional              // 使用注解方式管理事务public void transfer(String str, String target, Boolean flag) {jdbcTemplate.update("update account set money=money-500 where name=?", str);if (flag) {// 模拟异常int i = 1 / 0;}jdbcTemplate.update("update account set money=money+500 where name=?", target);}
}

3)测试类

package com.dfbz.test;import com.dfbz.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;/*** @author lscl* @version 1.0* @intro:*/
@RunWith(SpringRunner.class)
@ContextConfiguration(locations = "classpath:spring2.xml")
public class Demo02 {@Autowiredprivate AccountService accountService;@Testpublic void test1(){accountService.transfer("a","b",false);}
}

相关内容

热门资讯

发现玩家!德扑之星辅助器app... 发现玩家!德扑之星辅助器app透视挂,德扑之星辅助器app竟然真的有透视挂插件1、用户打开应用后不用...
热点讨论!约局吧德州真的有透视... 热点讨论!约局吧德州真的有透视挂吗辅助,约局吧德州真的有透视挂吗竟然是真的有辅助插件1、约局吧德州真...
热点推荐!哈糖大菠萝万能挂透视... 热点推荐!哈糖大菠萝万能挂透视,哈糖大菠萝万能挂总是是真的有透视攻略1、首先打开哈糖大菠萝万能挂辅助...
今日焦点!hardrock透视... 今日焦点!hardrock透视工具透视,hardrock透视工具果然是有透视技巧1、hardrock...
发现玩家!wpk模拟器多开辅助... 发现玩家!wpk模拟器多开辅助器,wpk模拟器多开其实存在有辅助器方法1、每一步都需要思考,不同水平...
一分钟了解!德扑之星透视辅助软... 一分钟了解!德扑之星透视辅助软件是真的吗开挂,德扑之星透视辅助软件是真的吗总是有开挂软件1)德扑之星...
教程辅助!德扑之星辅助正版透视... 教程辅助!德扑之星辅助正版透视挂,德扑之星辅助正版果然是有透视挂插件1、许多玩家不知道德扑之星辅助正...
一分钟了解!约局吧怎么看有没有... 一分钟了解!约局吧怎么看有没有挂透视,约局吧怎么看有没有挂原来存在有透视技巧;1、完成约局吧怎么看有...
六分钟了解!德扑之星app安卓... 六分钟了解!德扑之星app安卓版破解版辅助,德扑之星app安卓版破解版好像是真的有辅助教程在进入德扑...
发现一款!德扑之星辅助功能如何... 发现一款!德扑之星辅助功能如何设置透视,德扑之星辅助功能如何设置原来是真的有透视教程1、用户打开应用...