Spring事务管理 -- Spring快速入门保姆级教程(五)

这篇具有很好参考价值的文章主要介绍了Spring事务管理 -- Spring快速入门保姆级教程(五)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言

为了巩固所学的知识,作者尝试着开始发布一些学习笔记类的博客,方便日后回顾。当然,如果能帮到一些萌新进行新技术的学习那也是极好的。作者菜菜一枚,文章中如果有记录错误,欢迎读者朋友们批评指正。
(博客的参考源码可以在我主页的资源里找到,如果在学习的过程中有什么疑问欢迎大家在评论区向我提出)

发现宝藏

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。【宝藏入口】。

1. Spring事务简介

一个数据库事务是一个被视为单一的工作单元的操作序列。这些操作应该要么完整地执行,要么完全不执行。事务管理是一个重要组成部分,RDBMS 面向企业应用程序,以确保数据完整性和一致性。事务的概念可以描述为具有以下四个关键属性说成是 ACID:

原子性:事务应该当作一个单独单元的操作,这意味着整个序列操作要么是成功,要么是失败的。

一致性:这表示数据库的引用完整性的一致性,表中唯一的主键等。

隔离性:可能同时处理很多有相同的数据集的事务,每个事务应该与其他事务隔离,以防止数据损坏。

持久性:一个事务一旦完成全部操作后,这个事务的结果必须是永久性的,不能因系统故障而从数据库中删除。

一个真正的 RDBMS 数据库系统将为每个事务保证所有的四个属性。使用 SQL 发布到数据库中的事务的简单视图如下:

使用 begin transaction 命令开始事务。

使用 SQL 查询语句执行各种删除、更新或插入操作。

如果所有的操作都成功,则执行提交操作,否则回滚所有操作。

Spring 框架在不同的底层事务管理 APIs 的顶部提供了一个抽象层。Spring 的事务支持旨在通过添加事务能力到 POJOs 来提供给 EJB 事务一个选择方案。Spring 支持编程式和声明式事务管理。EJBs 需要一个应用程序服务器,但 Spring 事务管理可以在不需要应用程序服务器的情况下实现,实现接口及实现类如下:

Spring事务管理 -- Spring快速入门保姆级教程(五)

2. 入门案例–模拟银行间转账业务

1. 案例需求以及实现思路

Spring事务管理 -- Spring快速入门保姆级教程(五)

2. 环境准备

  • 创建数据库表tbl_account

建表查询语句示例

create database spring_db;
use spring_db;

drop table if exists tbl_account;

create table tbl_account(
	id int primary key auto_increment,
	name varchar(20),
	money varchar(20)
);

INSERT INTO tbl_account VALUES (1, 'Tom', 1000);
INSERT INTO tbl_account VALUES (2, 'Jerry', 1000);

效果

Spring事务管理 -- Spring快速入门保姆级教程(五)

  • 在pom.xml文件中导入坐标
 <dependencies>
    //spring框架
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>
    
    //druid数据源
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.16</version>
    </dependency>

    //mybatisa框架
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.6</version>
    </dependency>

    //数据库连接
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.47</version>
    </dependency>

    //spring操作jdbc
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>

    //spring整合mybatis
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.0</version>
    </dependency>

    //spring整合junit单元测试
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    
    //单元测试
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>

  </dependencies>
  • 创建spring全局配置文件SpringConfig
 @Configuration
 @ComponentScan("com.itheima")
 @PropertySource("classpath:jdbc.properties")
 @Import({JdbcConfig.class,MybatisConfig.class})
 //开启注解式事务驱动
 @EnableTransactionManagement
 public class SpringConfig {
 }

  • 创建数据库信息文件jdbc.properties,填写数据库连接信息
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_db?useSSL=false
jdbc.username=root
jdbc.password=root
  • 创建数据库连接配置类JdbcConfig
public class JdbcConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String userName;
    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }

    //配置事务管理器,mybatis使用的是jdbc事务
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource){
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}
  • 创建mybatis创建SqlSessionFactory Bean 和 Mapper 代理开发映射对象配置文件MybatisConfig
public class MybatisConfig {

    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
        ssfb.setTypeAliasesPackage("org.example.domain");
        ssfb.setDataSource(dataSource);
        return ssfb;
    }

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        msc.setBasePackage("org.example.dao");
        return msc;
    }
}

  • 根据数据库表创建对应的实体类Account
public class Account{
    //此处省略getter、setter和toString方法
    private Integer id;
    private String name;
    private Double money;
    
}
  • 创建AccontDao接口,用注解的方式定义mapper代理开发业务接口,分别为加钱的操作方法 inmoney 和 减钱的操作 outmoney
public interface AccountDao {

    @Update("update tbl_account set money = money + #{money} where name = #{name}")
    void inMoney(@Param("name") String name, @Param("money") Double money);

    @Update("update tbl_account set money = money - #{money} where name = #{name}")
    void outMoney(@Param("name") String name, @Param("money") Double money);
}
  • 创建业务层接口AccountService,定义转账操作方法transfer
public interface AccountService {
    /**
     * 转账操作
     * @param out 传出方
     * @param in 转入方
     * @param money 金额
     */
    //配置当前接口方法具有事务
    @Transactional
    public void transfer(String out,String in ,Double money) ;
}

  • 创建业务层接口 AccountService 的实现类AccountServiceImpl,调用数据层功能的加钱和减钱方法 inmoney 和 outmoney
@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;

    public void transfer(String out,String in ,Double money) {
        //减钱
        accountDao.outMoney(out,money);
        //加钱
        accountDao.inMoney(in,money);
    }

}

  • 编写测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest {

    @Autowired
    private AccountService accountService;

    @Test
    public void testTransfer() throws IOException {
        //Tom 向 Jerry 转100元
        accountService.transfer("Tom","Jerry",100D);
    }

}
  • 程序运行结果

Spring事务管理 -- Spring快速入门保姆级教程(五)
Spring事务管理 -- Spring快速入门保姆级教程(五)

  • 文件结构参考

Spring事务管理 -- Spring快速入门保姆级教程(五)

3. 入门案例分析

  • 入门案例完成了简单的模拟转账操作,此时我们在转账接口的实现类AccountServiceImpl减钱和加钱的操作中间添加一行模拟业务运行异常的代码,此时我们希望业务异常,终止操作,即数据库的账户余额不变。

  • 添加了模拟异常的代码及运行结果
    Spring事务管理 -- Spring快速入门保姆级教程(五)
    Spring事务管理 -- Spring快速入门保姆级教程(五)

Spring事务管理 -- Spring快速入门保姆级教程(五)

  • 通过观察数据库中的表格数据我们发现异常前面的减钱操作运行了,而异常后面的加钱操作没有运行,导致整体业务功能失败。我们可以通过开启Spring事务来解决这个问题。

4. 小结

  • Spring注解式事务通常添加在业务层接口中而不会添加到业务层实现类中,降低耦合
  • 注解式事务可以添加到业务方法上表示当前方法开启事务,也可以添加到接口上表示当前接口所有方法开启事务
  • 事务管理器需要根据实现技术进行选择,mybatis使用的是jdbc事务

3. 开启Spring事务的一般步骤

1. @Transactional 注解配置对应的接口方法具有事务

    @Transactional
    public void transfer(String out,String in ,Double money) ;
}

2. 配置事务管理器,mybatis使用的是jdbc事务

public class JdbcConfig {
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource){
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}

3. @EnableTransactionManagement 注解开启注解式事务驱动

@EnableTransactionManagement
public class SpringConfig {

}

此时在存在模拟异常的情况下再次运行模拟测试类,数据库中的数据不变,业务功能运行正常,Spring事务开启成功。

4. Spring事务角色

1. Spring事务角色分为事务管理员和事务协调员

  • 事务管理员:发起事务方,在Spring中通常指代业务层开启事务的方法

  • 事务协调员:加入事务方,在Spring中通常指代数据层方法,也可以是业务层方法

2. 结合入门案例分析

Spring事务管理 -- Spring快速入门保姆级教程(五)

  • 在入门案例中,没加@Transactional注解开启事务的情况下,减钱的outmoney方法、加钱的方法inmoney以及transfer接口都会各自开启一个事务,各自独立。当运行加钱事务出现异常的时候,减钱操作并不会加入加钱操作的事务回滚中,导致整个业务运行失败。

  • 当只用@Transactional注解开启事务后,事务减钱事务TI与加钱事务T2都加入事务T,成为一个事务,这样当事务中的加钱事务部分出现问题时,整个事务回滚,程序终止运行,整体业务正常运行成功。

5. spring事务属性–rollbackfor

1. 属性简介

属性 作用 示例
readonly 设置是否为只读事务 readOnly=true 只读事务
timeout 设置事务超时时间 timeout=-1(永不超时)
rollbackfor 设置事务回滚异常(class) {NullPointException.class rollbackFor=
rollbackForClassName 设置事务回滚异常(string) 同上格式为字符串
noRollbackFor 设置事务不回滚异常(class) noRollbackFor= (NullPointException.class}
noRollbackForClassName 设置事务不回滚异常(string) 同上格式为字符串
propagation 设置事务传播行为

2. 添加事务属性格式举例

public interface AccountService {
    /**
     * 转账操作
     * @param out 传出方
     * @param in 转入方
     * @param money 金额
     */
    //配置当前接口方法具有事务
    @Transactional(readOnly = true,timeout = -1)
    public void transfer(String out,String in ,Double money) ;
}

3. rollbackfor 属性介绍

  • 在业务层实现类中抛出一个IOException异常
@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    public void transfer(String out,String in ,Double money) throws IOException {
        accountDao.outMoney(out,money);
        //模拟抛出IOException异常
        if(true){throw new IOException();}
        accountDao.inMoney(in,money);
    }

}

  • 运行模拟测试类

Spring事务管理 -- Spring快速入门保姆级教程(五)
Spring事务管理 -- Spring快速入门保姆级教程(五)

  • 观察数据库表我们发现,开启事务后在遇到抛IOException类型的异常时,事务不会主动回滚,导致整体业务失败,这时候需要主动去配置。
public interface AccountService {
    /**
     * 转账操作
     * @param out 传出方
     * @param in 转入方
     * @param money 金额
     */
    //配置当前接口方法具有事务
    @Transactional(rollbackFor = {IOException.class})
    public void transfer(String out,String in ,Double money) throws IOException;
}

此时再次运行模拟测试类,当检测到IOExctpition类型的异常时,事务T回滚,数据库表中的数据不变。

  • 开启Spring事务只回滚两种事务:ERROR系列(如内存溢出) 和 运行时异常(如NO Point Exception)。其余的均需要通过rollbackfor属性手动配置。

6. 入门案例进阶–转账业务追加日志

1. 案例需求及实现思路

Spring事务管理 -- Spring快速入门保姆级教程(五)

2. 编码环境准备

  • 创建数据库表tbl_log

sql建表语句

use spring_db;

drop table if exists tbl_log;

create table tbl_log(
	id int primary key auto_increment,
	info varchar(100),
	createDate varchar(20)
);

INSERT INTO tbl_log VALUES (1, '转账操作由TomJerry,金额:100.0', '20:55:49');

表格示例

Spring事务管理 -- Spring快速入门保姆级教程(五)

  • LogDao数据层接口
public interface LogDao {
    @Insert("insert into tbl_log (info,createDate) values(#{info},now())")
    void log(String info);
}

  • LogService 业务层接口
public interface LogService {
    @Transactional
    void log(String out, String in, Double money);
}

  • LogService 业务层接口实现类
@Service
public class LogServiceImpl implements LogService {

    @Autowired
    private LogDao logDao;

    public void log(String out,String in,Double money ) {
        logDao.log("转账操作由"+out+"到"+in+",金额:"+money);
    }
}
  • 将IOException相关的代码注释掉,保留最基础的Spring事务代码

  • 文件结构示例

Spring事务管理 -- Spring快速入门保姆级教程(五)

3. 用try…finally结构编写业务层实现类AccountServiceImpl 并运行测试类

@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    @Autowired
    private LogService logService;

    public void transfer(String out,String in ,Double money) {
        try{
            accountDao.outMoney(out,money);
            int i = 1/0;
            accountDao.inMoney(in,money);
        }finally {
            logService.log(out,in,money);
        }
    }

}

  • 将数据库中的金额分别手动修改重置到1000,并执行测试类

Spring事务管理 -- Spring快速入门保姆级教程(五)
Spring事务管理 -- Spring快速入门保姆级教程(五)

Spring事务管理 -- Spring快速入门保姆级教程(五)
Spring事务管理 -- Spring快速入门保姆级教程(五)

7. Spring事务属性–事务传播行为

1. 探讨添加了异常后,日志能否正常加载

Spring事务管理 -- Spring快速入门保姆级教程(五)

  • 运行结果分析

此时再次运行测试类,我们可以发现数据库表中金额表数值由于开启了事务,当出现异常时,事务回滚,金额数值不变。同时日志表的数据也没有发生变化,不符合我们的预期。

  • 存在的问题:

日志的记录与转账操作隶属同一个事务,同成功同失败

Spring事务管理 -- Spring快速入门保姆级教程(五)

  • 实现效果预期改进:

无论转账操作是否成功,日志必须保留,这时就需要通过事务传播行为来解决这个问题

Spring事务管理 -- Spring快速入门保姆级教程(五)

2. spring事务的7种传播行为

required (默认传播行为),如果当前有事务,其他就用当前事务,不会新增事务。例如:方法A调用方法B,它们用同一个事务。(如果B没有事务,它们会用同一个事务。)(只要有一个回滚,整体就会回滚)
requires_new 如果当前有事务,其他不会加入当前事务,会新增事务。即他们的事务没有关系,不是同一个事务。如果其他没有事务,那么以当前事务运行。例如:方法A调用方法B,它们用不同的事务。(B不会用A的事务,会新增事务)
supports 当前没有事务,就以非事务运行。当前有事务呢?就以当前事务运行。例如:方法A调用方法B,如果A没有事务,那么B就以非事务运行。如果A有事务就以A事务为准。如果A没有事务,那么B就会以非事务执行。
mandatory 其他没有事务就会抛异常。当前没有事务抛出异常,当前有事务则支持当前事务。支持当前事务,如果当前没有事务就会抛出异常。例如:方法A调用方法B,如果方法A没有事务,那么就会抛出异常。
not_supported 以非事务执行。例如:方法A调用方法B,方法B会挂起事务A以非事务方式执行。
never 以非事务执行,如果存在事务,抛出异常。总是以非事务执行,如果存在事务,那么就抛出异常。
nested 如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与Required一样。例如:方法A中调用了方法B,B中try catch手动回滚,A不会回滚。

3. 设置日志接口方法的事务传播行为属性

public interface LogService {
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    void log(String out, String in, Double money);
}
  1. 运行测试类观察效果

Spring事务管理 -- Spring快速入门保姆级教程(五)
Spring事务管理 -- Spring快速入门保姆级教程(五)
Spring事务管理 -- Spring快速入门保姆级教程(五)
基于以上结果,Spring事务传播行为设置成功,业务功能运行正常。

`博客内容借鉴了bilibili黑马程序员SSM课程资料`

总结

欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下。
(博客的参考源码可以在我主页的资源里找到,如果在学习的过程中有什么疑问欢迎大家在评论区向我提出)文章来源地址https://www.toymoban.com/news/detail-478209.html

到了这里,关于Spring事务管理 -- Spring快速入门保姆级教程(五)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Spring】Spring的事务管理

    Spring的事务管理简化了传统的事务管理流程,并且在一定程度上减少了开发者的工作量。 1.1 事务管理的核心接口 在Spring的所有JAR包中包含一个名为Spring-tx-4.3.6.RELEASE的JAR包,该包就是Spring提供的用于事务管理的依赖包。在该JAR包的org.Springframework.transaction包中有3个接口文件:

    2024年02月03日
    浏览(95)
  • Spring 事务管理

    目录 1. 事务管理 1.1. Spring框架的事务支持模型的优势 1.1.1. 全局事务 1.1.2. 本地事务 1.1.3. Spring框架的一致化编程模型 1.2. 了解Spring框架的事务抽象(Transaction Abstraction) 1.2.1. Hibernate 事务设置 1.3. 用事务同步资源 1.3.1. 高级同步方式 1.3.2. 低级同步方式 1.3.3.TransactionAwareDataSo

    2024年02月13日
    浏览(45)
  • Spring的事务管理

    1、事务的回顾 【1】事务的定义 是数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作,这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行;事务是一组不可再分割的操作集合 【2】事务的ACID原则 事务具有4个基本特性:原子性、一致性

    2023年04月16日
    浏览(74)
  • Spring之事务管理

    事务是数据库操作最基本单位,要么都成功,要么都失败。 原子性 一致性 隔离性 持久性。 Spring定义了7种传播行为: 传播属性 描述 REQUIRED 如果有事务在运行,当前的方法就在这个事务内运行,否则,就启动一个新的事务,并在自己的事务内运行 REQUIRED_NEW 当前的方法必须

    2024年02月13日
    浏览(66)
  • Spring使用@Transactional 管理事务,Java事务详解。

    B站视频:https://www.bilibili.com/video/BV1eV411u7cg 技术文档:https://d9bp4nr5ye.feishu.cn/wiki/HX50wdHFyiFoLrkfEAAcTBdinvh 简单来说事务就是一组对数据库的操作 要么都成功,要么都失败。 事务要保证可靠性,必须具备四个特性:ACID。 A:原子性:事务是一个原子操作单元,要么完全执行,要么

    2024年02月11日
    浏览(29)
  • 【掌握Spring事务管理】深入理解事务传播机制的秘密

    🎉🎉🎉 点进来你就是我的人了 博主主页: 🙈🙈🙈 戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔 🤺🤺🤺 目录 1.Spring 中事务的实现方式 1.1 Spring 编程式事务 (了解) 1.2 Spring 声明式事务 ( @Transactional ) 【异常情况一】(自动回滚成功) 【异常情况二】(自动回滚失效

    2024年02月10日
    浏览(46)
  • Spring中的事务管理

    概念 事务 是一组操作的集合,它是一个不可分割的工作单位,这些操作要么 同时成功 ,要么 同时失败 。 操作 开启事务(一组操作 开始前 ,开启事务) : start transaction / begin ; 提交事务(这组操作 全部成功后 ,提交事务) : commit ; 回滚事务(中间任何一个操作 出现异常 ,

    2024年02月12日
    浏览(34)
  • Spring 事务管理详解及使用

    ✅作者简介:2022年 博客新星 第八 。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏:SSM 框架从入门到精通 ✨特色专栏:国学周更-心性养成之路 🥭本文内容:一文吃透 Spring 中

    2024年01月20日
    浏览(37)
  • 【Spring篇】AOP事务管理

    🍓系列专栏:Spring系列专栏 🍉个人主页:个人主页 1.Spring事务简介 1.相关概念介绍 事务作用:在数据层保障一系列的数据库操作同成功同失败 Spring事务作用:在数据层或 业务层 保障一系列的数据库操作同成功同失败 数据层有事务我们可以理解,为什么业务层也需要处理事务

    2024年02月06日
    浏览(42)
  • Spring管理事务知识

    目录 1.什么是事务 2.事务的特性ACID 3.Spring 管理事务的方式 4.Spring管理事务的体现:JDBCTemplate 5.声明式事务的属性有哪些 6.声明式事务属性---只读 7.声明式事务属性---超时 8.声明式事务属性---回滚策略 9.声明式事务属性---事务隔离级别 10.声明式事务属性---事务传播行为  概念

    2024年02月11日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包