Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >8-Spring事务控制

8-Spring事务控制

作者头像
Ywrby
发布于 2022-10-27 05:25:14
发布于 2022-10-27 05:25:14
31000
代码可运行
举报
文章被收录于专栏:YwrbyYwrby
运行总次数:0
代码可运行

Spring事务控制

事务概念

概括来讲,事务是一个由有限操作集合组成的逻辑单元。事务操作包含两个目的,数据一致以及操作隔离。数据一致是指事务提交时保证事务内的所有操作都成功完成,并且更改永久生效;事务回滚时,保证能够恢复到事务执行之前的状态。操作隔离则是指多个同时执行的事务之间应该相互独立,互不影响。

事务是一个比较广泛的概念,事务管理资源除了我们熟知的数据库外,还可以包含消息队列文件系统等。当然,一般来说,我们说的事务单指“数据库事务”。

事务的ACID属性

  • 原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。
  • 一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。
  • 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
  • 持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库中。

编程式事务控制

Spring编程式事务控制就是指利用Spring提供的API进行事务控制,通过编写Java代码的方式完成,这种事务控制方法相对较灵活,但不便于管理,即耦合度较高

PlatformTransactionManager 平台事务管理器

PlatformTransactionManager接口是Spring的事务管理器类,内部提供了我们常用的操作事务的方法

方法

说明

TransactionStatus getTransaction(TransactionDefination defination)

获取事务状态信息

void commit(TransactionStatus status)

获取事务状态信息

void rollback(TransactionStatus status)

回滚事务

根据dao层的不同技术实现(例如JDBC或mybatis…),PlatformTransactionManager接口实现了不同的实现类

TransactionDefination

TransactionDefination是事务的定义信息对象,实现了如下方法

方法

说明

int getIsolationLevel()

获得事务的隔离级别

int getPropogationBehavior()

获得事务的传播行为

int getTimeout()

获得超时时间

boolean isReadyOnly()

是否只读

事务的隔离级别

设置事务级别,用于解决事务并发产生的问题。如脏读,不可重复度,虚读…

  • ISOLATION_DEFAULT:默认隔离级别
  • ISOLATION_READ_UNCOMMITTED:读未提交
  • ISOLATION_READ_COMMITTED:读已提交(可以解决脏读)
  • ISOLATION_REPEATABLE_READ:可重复读(可以解决不可重复读问题)
  • ISOLATION_SERIALIZABLE:串行化(都可以解决 但效率低下)
事务的传播行为

事务传播行为用于描述当一个事务传播行为的修饰方法其他方法调用时,事务是如何传播的

Spring中提供了其中事务传播的行为

事务传播行为

说明

REQUIRED

如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)

SUPPORTS

支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)

MANDATORY

使用当前的事务,如果当前没有事务,就抛出异常

REQUERS_NEW

新建事务,如果当前在事务中,把当前事务挂起。

NOT_SUPPORTED

以非事务方式执行操作,如果当前存在事务,就把当前事务挂起

NEVER

以非事务方式运行,如果当前存在事务,抛出异常

NESTED

如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行 REQUIRED 类似的操作

  • 超时时间:默认值是-1,没有超时限制。如果有,以秒为单位进行设置
  • 是否只读:建议查询时设置为只读

TransactionStatus

TransactionStatus接口提供了事务具体的运行状态

方法

说明

boolean hasSavepoint()

是否存储回滚点

boolean isCompleted()

事务是否完成

boolean isNewTransaction()

是否是新事务

boolean isRollBackOnly()

事务是否回滚

Spring声明式事务控制

Spring的声明式事务控制就是指利用声明的方式进行事务控制,这里所指的声明就是利用Spring配置文件或注解的方式进行配置

声明式事务控制的作用

事务管理是属于系统层面的服务,而我们所编写的业务逻辑对象是属于业务逻辑层面的,如果使用编程式事务控制,就需要将事务管理和业务逻辑对象一起进行编写,二者将被耦合死。二通过声明式事务控制,则可以通过配置的方式,在配置文件中编写如何利用业务逻辑对象进行事务管理,此时业务逻辑对象并不会意识到自己正在执行相关事务,即实现了解耦合(业务逻辑与事务管理之间)

这个过程实际上还是遵循了Spring中的AOP,整个过程是为了实现对方法的增强,而增强的方式就是通过使用事务,即业务逻辑对象是切点,事务是通知(增强)

Spring声明式事务控制的底层就是AOP

基于XML方式的声明式事务控制

以银行转账事务为例进行基于XML方式的声明式事务控制

dao层:定义数据库操作方法

in方法为修改入帐方的余额

out方法为修改出账方发余额

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface AccountDao {
    public void in(String inMan,double money);
    public void out(String outMan,double money);
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Component("accountDao")
public class AccountDaoImpl implements AccountDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public void in(String inMan, double money) {
        jdbcTemplate.update("update account set money=money+? where name =?",money,inMan);
    }

    public void out(String outMan, double money) {
        jdbcTemplate.update("update account set money=money-? where name =?",money,outMan);
    }
}

Service层

transfer方法就是转账方法,接收出账方,入帐方和金额三个参数

transfer方法调用了in方法和out方法,这两个方法各自为一个事务,此时如果不进行事务控制,可以看到由于二者之间故意设置的除数错误,会导致入账事务执行并完成,但出账事务未进行。

因此必须通过事务控制增强这个方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface AccountService {
    public void transfer(String outMan,String inMan,double money);
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Component("accountService")
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDaoImpl accountDao;

    public void setAccountDao(AccountDaoImpl accountDao) {
        this.accountDao = accountDao;
    }

    public void transfer(String outMan, String inMan, double money) {
        accountDao.in(inMan,money);
        int i=1/0;
        accountDao.out(outMan,money);
    }
}

配置文件

首先要引入AOP命名空间用于进行事务的织入,其次还需要引入tx命名空间进行事务通知的定义

然后就是需要配置平台事务管理器并为其配置数据源以进行事务管理,并且要定义通知(在tx命名空间中)

最后进行织入,将通知织入指定方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?xml version="1.0" encoding="UTF-8"?>
<!--引入tx命名空间,用于进行事务的管理-->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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/tx http://www.springframework.org/schema/tx/spring-tx.xsd
                           http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--配置注解扫描-->
    <context:component-scan base-package="cn.ywrby"/>

    <!--加载properties配置文件(classpath表示的就是资源目录resources下)-->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--配置数据源-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${driver}"></property>
        <property name="jdbcUrl" value="${url}"></property>
        <property name="user" value="${user}"></property>
        <property name="password" value="${password}"></property>
    </bean>

    <!--配置JdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

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

    <!--通知 用于进行事务的增强-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--设置事务的属性信息-->
        <tx:attributes>
            <!--name设置增强的方法 *表示所有方法均进行增强-->
            <!--isolation表示隔离级别-->
            <!--propagation表示事务的传播行为-->
            <!--timeout表示超过时间-->
            <!--read-only表示是否只读-->
            <tx:method name="*" isolation="DEFAULT" propagation="REQUIRED" timeout="-1" read-only="false" />
        </tx:attributes>
    </tx:advice>

    <!--配置AOP事务的织入:将事务织入到业务逻辑对象的方法中-->
    <aop:config>
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* cn.ywrby.service.impl.*.*(..))"></aop:advisor>
    </aop:config>
</beans>

基于注解的Spring事务控制

基于注解的Spring事务控制需要修改两处,首先在需要进行事务控制的方法或类上利用@Transactional注解表示对该方法进行事务控制,(其内可以传入参数进行属性的配置)

可以看到,类上和方法上都可以使用该注解,当在类上使用该注解时表示该类内所有方法均按此配置进行事务控制,同时若方法上另有配置,则遵循方法上的配置(就近原则)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Component("accountService")
@Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.MANDATORY,readOnly = false,timeout = -1)
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDaoImpl accountDao;

    public void setAccountDao(AccountDaoImpl accountDao) {
        this.accountDao = accountDao;
    }

    @Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED)
    public void transfer(String outMan, String inMan, double money) {
        accountDao.in(inMan,money);
        int i=1/0;
        accountDao.out(outMan,money);
    }
}

其次还需要修改配置文件,以进行事务的注解驱动声明

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?xml version="1.0" encoding="UTF-8"?>
<!--引入tx命名空间,用于进行事务的管理-->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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/tx http://www.springframework.org/schema/tx/spring-tx.xsd
                           http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--配置注解扫描-->
    <context:component-scan base-package="cn.ywrby"/>

    <!--加载properties配置文件(classpath表示的就是资源目录resources下)-->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--配置数据源-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${driver}"></property>
        <property name="jdbcUrl" value="${url}"></property>
        <property name="user" value="${user}"></property>
        <property name="password" value="${password}"></property>
    </bean>

    <!--配置JdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

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

    <!--声明事务的注解驱动-->
    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-03-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
《Monkey Java》课程5.5之对象的转型
本节课程将学习以下内容: 对象的向上转型 对象的向下转型 ---- 对象的向上转型 概念: 将子类的对象赋值给父类的引用。 注意: 两个类一定要有继承关系; 将子类的对象赋值给父类的引用; 一个引用能够调用哪些成员(变量、方法),取决于这个引用的类型; 一个引用调用的是哪个方法,取决于这个引用所指向的对象。 格式: // Student类是Person类的子类 Student s = new Student(); // 将子类的对象s赋值给父类的引用p; // 理解:每一个学生都是一个人。 Pe
GitOPEN
2019/01/29
3130
《Monkey Java》课程5.2之继承(extends)基础
本节课程将学习以下内容: 什么是继承 为什么要使用继承 继承的基本语法特点 ---- 继承的概念和格式 现实世界中,打个比方,继承就是富二代,就是儿子得到并使用老子的东西; 面向对象的世界中,继承就是一个类得到了另外一个类当中的成员变量和成员方法。 格式: class A{ 语句; } class B extends A{ 语句; } class C extends A{ 语句1; 语句2; } 注意:Java中只允许单继承,不允许多继承。 例子:(请动手) 1.新建一个名为Pe
GitOPEN
2019/01/29
4320
《Monkey Java》课程5.3之子类实例化
本节课程将学习以下内容: 生成子类的过程 使用super调用父类构造函数的方法 ---- 生成子类的过程 使用super调用父类构造函数的方法 注意: 在子类的构造函数中,必须调用父类的构造函数; super所调用的是父类的哪个构造函数,是由super(参数)中的参数个数决定; super(参数);必须是构造函数的第一行。 例子:(请动手) 1.新建一个名为Person.java的Java源文件: class Person{ String name; int age; Person(){
GitOPEN
2019/01/29
4770
《Monkey Java》课程5.0之this的使用方法
本节课程将学习以下内容: this的涵义 使用this调用成员变量和成员函数 使用this调用构造函数 ---- this的涵义 this代表一个对象,它代表了调用当前类中的变量或者函数的对象。 使用this调用成员变量和成员函数 例子:(请动手) 1.新建一个名为Person.java的Java源文件: class Person{ String name; void talk(){ // 这里的this就是一个Person的对象。 System.out.println("M
GitOPEN
2019/01/29
4320
《Monkey Java》课程6.0之抽象类和抽象函数
本节课程将学习以下内容: 抽象函数的语法特征 抽象类的语法特征 抽象类的作用 ---- 抽象函数的语法特征 定义: 只有函数的定义(返回值类型、函数名、参数列表),没有函数体的函数被称为抽象函数。 特点: 如果一个类中有一个或者一个以上的抽象函数,那么这个类也必须被定义为抽象类; 格式: abstract void function(); 抽象类的语法特征 定义: 使用abstract定义的类(在class前加上abstract关键字)称之为抽象类。 特点: 抽象类天生就是用来被继承的
GitOPEN
2019/01/29
5370
《Monkey Java》课程4.3之面向对象基础4
本节课程将学习以下内容: 函数的重载 构造函数的作用 ---- 函数的重载 在同一个类中允许函数的重名这种现象的出现,它有3个特征: 两个或者多个函数在同一个类当中; 函数名相同; 参数列表不同。 例子:(请动手) 1.新建一个名称为Demo01.java的java源文件。 class Demo01 { void funDemo01() { System.out.println("没有参数的funDemo01函数"); } // 这个funDemo01函数重载了上面的funD
GitOPEN
2019/01/29
2700
《Monkey Java》课程6.2之访问权限
本节课程将学习以下内容: Java当中的访问权限 软件包的导入 ---- Java当中的访问权限 一共分为4大类: public:公共权限,既可以修饰类,也可以修饰成员变量和成员函数; private:私有权限 default:包级别访问权限 protected:受保护权限 public权限 如果一个类不是public(公共)的,那么这个类不能被外部软件包访问; 如果一个类的成员变量或者方法不是public(公共)的,那么它们也不能被外部软件包访问; 没有任何限制,同一个包中,或者不同软件包当中,都
GitOPEN
2019/01/29
4510
《Monkey Java》课程6.3之protected权限
本节课程将学习以下内容: protected权限 ---- protected权限 protected权限首先拥有和default一样的功能; 但是它只能修饰成员变量和成员函数; 允许跨软件包的继承; 只允许类的子类继承使用它的protected成员属性和函数; 例子:(请动手) 1、新建一个Person.java源文件: package com.sunjiajia; public class Person{ protected String name; protected int age;
GitOPEN
2019/01/29
4380
Android入门教程(五)
在for循环当中,每当循环执行一次,就判断循环变量是否为素数,如果是,就将循环变量的当前值打印出来;
达达前端
2022/04/28
3890
《Monkey Java》课程5.1之static关键字的作用
本节课程将学习以下内容: 静态成员变量的语法特点 静态函数的语法特点 静态代码块的语法特点 ---- 静态成员变量的语法特点 在定义成员变量的时候,在前面添加一个static关键字。 格式:st
GitOPEN
2019/01/29
4310
《Monkey Java》课程5.1之static关键字的作用
[ Java学习基础 ] Java的继承与多态
看到自己写的东西(4.22的随笔[ Java学习基础 ] Java构造函数)第一次达到阅读100+的成就还是挺欣慰的,感谢大家的支持!希望以后能继续和大家共同学习,共同努力,一起进步!共勉! ------------------------------------ 一、Java继承       继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。       继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有
Kevin_Zhang
2018/05/22
1K0
java之父类中的构造器是否能被子类继承?
子类默认继承父类的属性和方法,但不会继承父类的构造器,而是在子类被实例化时会默认调用父类的空构造器。子类在创建的时候会调用自己的空构造器,并在空构造器会隐式调用super(),即父类的空构造器。如果父类的构造器被重载,那么子类中的构造器也必须初始化父类的构造器,否则会报编译错误。当然,只要父类中显示定义了空构造器,子类中就不必初始化父类的构造器。例如: 父类Person.java
西西嘛呦
2020/08/26
1.2K0
大话Java异常
@toc 异常 异常的概述 --- 异常就是不正常的意思,Java语言中主要是指程序在运行阶段产生的错误 Throwable(可抛出的,可扔出的) - java.lang.Throwable 类是Java程序所有错误或异常的超类 - 主要有两个字类 - Error - Error主要描述比较严重的错误 - 无法通过编程来解决的重大的错误 - Exception - Exception主要m描述比较轻量级的错误 - 可以通过编程来解决 Exception类的
ruochen
2021/05/15
7030
大话Java异常
第二十八节:Java基础-进阶继承,抽象类,接口
在子类和父类中都有构造函数,运行时,先执行父类中的构造函数,在执行子类的构造函数,因为子类的所有构造函数中第一行都有一句,隐式的super();,表示为调用父类的无参的构造函数。
达达前端
2019/07/03
3570
第二十八节:Java基础-进阶继承,抽象类,接口
java之方法的重写
在子类中可以根据需要对从父类中继承而来的方法进行改造,也称为重写。在执行程序时,子类的方法将覆盖父类的方法。
西西嘛呦
2020/08/26
7970
第五节:详细讲解Java中的接口与继承
大家好,我是 Vic,今天给大家带来详细讲解Java中的接口与继承的概述,希望你们喜欢
达达前端
2019/07/03
3970
面向对象详解,面向对象的三大特征:封装、继承、多态
面向对象编程(Object-Oriented Programming,简称OOP)和面向过程编程(Procedural Programming,简称PP)是两种不同的编程范式。
爱喝兽奶的熊孩子
2024/04/10
4.1K1
面向对象详解,面向对象的三大特征:封装、继承、多态
《Monkey Java》课程7.2之内部类和匿名内部类
本节课程将学习以下内容: 什么是内部类 内部类的使用方法 匿名内部类的使用方法 ---- 什么是内部类 含义: 一个类定义在另一个类内部。 例子1: 1.新建一个名为A.java的源文件: class A { int i; // B类就是内部类 class B { int j; int funB(){ // 内部类B可以使用外部类A的成员变量和成员方法 int result = i + j; return result; } } } 注意: 在编译A.jav
GitOPEN
2019/01/29
3060
【10】JAVASE-面向对象-继承【从零开始学JAVA】
Java 是第一大编程语言和开发平台。它有助于企业降低成本、缩短开发周期、推动创新以及改善应用服务。如今全球有数百万开发人员运行着超过 51 亿个 Java 虚拟机,Java 仍是企业和开发人员的首选开发平台。
用户4919348
2024/05/25
520
【10】JAVASE-面向对象-继承【从零开始学JAVA】
《Monkey Java》课程7.0之Java当中的异常
本节课程将学习以下内容: 什么是异常 异常的分类 try…catch…finally结构的使用方法 throw的作用 throws的作用 ---- 什么是异常 含义: 在面向对象的世界中,异常也是对象; 我们自己的话说,Java程序在正常运行中,出现的一些意外,被称为异常。 定义:中断了正常指令流的事件; 异常和语法错误是不同的东西; 语法错误在编译的时候就会报错;而异常是在运行的时候抛出来的。 格式: Exception in thread "main" java.lang.ArithmeticExc
GitOPEN
2019/01/29
4200
推荐阅读
相关推荐
《Monkey Java》课程5.5之对象的转型
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验