Spring之路(37)–在原生JDBC上使用事务

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
云数据库 RDS PostgreSQL,高可用系列 2核4GB
简介: 本文目录1. 概述2. 不使用事务3. 使用事务4. 默认情况

1. 概述

Spring事务是对原生事务的封装,我们还是需要了解如果直接使用JDBC的话,如何实现事务。


我们将向blog表插入两条数据(两次更新操作)定义为一个原子性操作,所以我们是期望这两个操作能同时成功、或者同时失败的。


2. 不使用事务

如果不使用事务,有可能会发生一个操作成功、另一个操作失败的情况,所以我们预期的原子性操作不成立。代码如下,可以看出因为执行过程中出现异常,导致最终只插入了一条数据,数据的完整性保护失败。

package org.maoge.jdbctran;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
/**
 * 原生JDBC事务使用实例
 */
public class JdbcTransactionDemo {
  public static void main(String[] args) {
    actionWithoutTransaction();
  }
  /**
   * 未使用事务
   */
  public static void actionWithoutTransaction() {
    String driver = "com.mysql.jdbc.Driver";
    String url = "jdbc:mysql://127.0.0.1:3306/myblog?useUnicode=true&characterEncoding=utf-8";
    String username = "root";
    String password = "Easy@0122";
    Connection conn = null;
    try {
      Class.forName(driver);
      conn = DriverManager.getConnection(url, username, password);
      String sql = "insert into blog(author,content,title)values(1,2,3)";
      Statement stmt = conn.createStatement();
      stmt.execute(sql);// 执行成功
      int temp = 1 / 0;// 模拟抛出异常
      stmt.execute(sql);// 未执行
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      if (conn != null) {
        try {
          conn.close();
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    }
  }
}

3. 使用事务

如果使用事务的话,示例如下:

package org.maoge.jdbctran;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/**
 * 原生JDBC事务使用实例
 */
public class JdbcTransactionDemo {
  public static void main(String[] args) {
    actionWithTransaction();
  }
  /**
   * 使用事务
   */
  public static void actionWithTransaction() {
    String driver = "com.mysql.jdbc.Driver";
    String url = "jdbc:mysql://127.0.0.1:3306/myblog?useUnicode=true&characterEncoding=utf-8";
    String username = "root";
    String password = "Easy@0122";
    Connection conn = null;
    try {
      Class.forName(driver);
      conn = DriverManager.getConnection(url, username, password);
      conn.setAutoCommit(false);// 定义事务的起点
      String sql = "insert into blog(author,content,title)values(1,2,3)";
      Statement stmt = conn.createStatement();
      stmt.execute(sql);// 没问题
      int temp = 1 / 0;// 模拟抛出异常,导致回滚,所以上面的执行也回滚了
      stmt.execute(sql);// 未执行
      conn.commit();// 如果全部执行成功,则提交事务
    } catch (Exception e) {
      try {
        conn.rollback();// 如果出现异常则回滚
      } catch (SQLException ex) {
        ex.printStackTrace();
      }
      e.printStackTrace();
    } finally {
      if (conn != null) {
        try {
          conn.close();
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    }
  }
}

分析上面的代码,就是我们在开始一个原子性操作之前(可能包含若干对数据库的操作),先开启事务,然后等几个操作都执行后再提交事务,此时对数据库的修改才生效;如果这中间发生异常,则执行回滚,则这些操作一个都不会生效。


从上面的示例中可以发现,几乎很多地方都是跟业务逻辑无关的代码,都是模板性质的代码,遵循如下结构:


try{

//开始事务

//执行具体操作

//提交事务

}catch(Exception e){

//回滚事务

//处理异常

}finally{

//释放资源

}

1

2

3

4

5

6

7

8

9

10

其中真正有意义的,只有具体操作部分,其他都是雷同的,所以可以封装起来。


4. 默认情况

那么如果不显示的指定conn.setAutoCommit(false);,是什么情况呢。我们尝试下,可见默认情况下事务是自动提交的,也就是执行一个sql就自动提交事务了,所以默认情况下执行sql后更新马上就持久化到数据库了。

package org.maoge.jdbctran;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/**
 * 原生JDBC事务使用实例
 */
public class JdbcTransactionDemo {
  public static void main(String[] args) {
    testAutoCommit();
  }
  /**
   * 使用事务
   */
  public static void testAutoCommit() {
    String driver = "com.mysql.jdbc.Driver";
    String url = "jdbc:mysql://127.0.0.1:3306/myblog?useUnicode=true&characterEncoding=utf-8";
    String username = "root";
    String password = "Easy@0122";
    Connection conn = null;
    try {
      Class.forName(driver);
      conn = DriverManager.getConnection(url, username, password);
      System.out.println(conn.getAutoCommit());//输出true
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      if (conn != null) {
        try {
          conn.close();
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    }
  }
}
相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
相关文章
|
29天前
|
SQL Java 关系型数据库
Spring事务传播机制:7种姿势教你玩转"事务接力赛"
事务传播机制是Spring框架中用于管理事务行为的重要概念,它决定了在方法调用时事务如何传递与执行。通过7种传播行为,开发者可以灵活控制事务边界,适应不同业务场景。例如:REQUIRED默认加入或新建事务,REQUIRES_NEW独立开启新事务,NESTED支持嵌套回滚等。合理使用传播机制不仅能保障数据一致性,还能提升系统性能与健壮性。掌握这“七种人格”,才能在复杂业务中游刃有余。
|
2月前
|
Java 关系型数据库 数据库
深度剖析【Spring】事务:万字详解,彻底掌握传播机制与事务原理
在Java开发中,Spring框架通过事务管理机制,帮我们轻松实现了这种“承诺”。它不仅封装了底层复杂的事务控制逻辑(比如手动开启、提交、回滚事务),还提供了灵活的配置方式,让开发者能专注于业务逻辑,而不用纠结于事务细节。
|
7月前
|
Java Spring
Spring中事务失效的场景
因为Spring事务是基于代理来实现的,所以某个加了@Transactional的⽅法只有是被代理对象调⽤时, 那么这个注解才会⽣效 , 如果使用的是被代理对象调用, 那么@Transactional会失效 同时如果某个⽅法是private的,那么@Transactional也会失效,因为底层cglib是基于⽗⼦类来实现 的,⼦类是不能重载⽗类的private⽅法的,所以⽆法很好的利⽤代理,也会导致@Transactianal失效 如果在业务中对异常进行了捕获处理 , 出现异常后Spring框架无法感知到异常, @Transactional也会失效
|
7月前
|
Java 关系型数据库 数据库
微服务——SpringBoot使用归纳——Spring Boot事务配置管理——常见问题总结
本文总结了Spring Boot中使用事务的常见问题,虽然通过`@Transactional`注解可以轻松实现事务管理,但在实际项目中仍有许多潜在坑点。文章详细分析了三个典型问题:1) 异常未被捕获导致事务未回滚,需明确指定`rollbackFor`属性;2) 异常被try-catch“吃掉”,应避免在事务方法中直接处理异常;3) 事务范围与锁范围不一致引发并发问题,建议调整锁策略以覆盖事务范围。这些问题看似简单,但一旦发生,排查难度较大,因此开发时需格外留意。最后,文章提供了课程源代码下载地址,供读者实践参考。
147 0
|
7月前
|
Java 关系型数据库 数据库
微服务——SpringBoot使用归纳——Spring Boot事务配置管理——Spring Boot 事务配置
本文介绍了 Spring Boot 中的事务配置与使用方法。首先需要导入 MySQL 依赖,Spring Boot 会自动注入 `DataSourceTransactionManager`,无需额外配置即可通过 `@Transactional` 注解实现事务管理。接着通过创建一个用户插入功能的示例,展示了如何在 Service 层手动抛出异常以测试事务回滚机制。测试结果表明,数据库中未新增记录,证明事务已成功回滚。此过程简单高效,适合日常开发需求。
966 0
|
7月前
|
Java 数据库 微服务
微服务——SpringBoot使用归纳——Spring Boot事务配置管理——事务相关
本文介绍Spring Boot事务配置管理,阐述事务在企业应用开发中的重要性。事务确保数据操作可靠,任一异常均可回滚至初始状态,如转账、购票等场景需全流程执行成功才算完成。同时,事务管理在Spring Boot的service层广泛应用,但根据实际需求也可能存在无需事务的情况,例如独立数据插入操作。
162 0
|
5月前
|
人工智能 Java 数据库连接
Spring事务失效场景
本文深入探讨了Spring框架中事务管理可能失效的几种常见场景及解决方案,包括事务方法访问级别不当、方法内部自调用、错误的异常处理、事务管理器或数据源配置错误、数据库不支持事务以及不合理的事务传播行为或隔离级别。通过合理配置和正确使用`@Transactional`注解,开发者可以有效避免这些问题,确保应用的数据一致性和完整性。
247 10
|
4月前
|
Java 关系型数据库 MySQL
【Spring】【事务】初学者直呼学会了的Spring事务入门
本文深入解析了Spring事务的核心概念与使用方法。Spring事务是一种数据库事务管理机制,通过确保操作的原子性、一致性、隔离性和持久性(ACID),维护数据完整性。文章详细讲解了声明式事务(@Transactional注解)和编程式事务(TransactionTemplate、PlatformTransactionManager)的区别与用法,并探讨了事务传播行为(如REQUIRED、REQUIRES_NEW等)及隔离级别(如READ_COMMITTED、REPEATABLE_READ)。
312 1
|
7月前
|
SQL Java 数据库连接
Spring中的事务是如何实现的
1. Spring事务底层是基于数据库事务和AOP机制的 2. ⾸先对于使⽤了@Transactional注解的Bean,Spring会创建⼀个代理对象作为Bean 3. 当调⽤代理对象的⽅法时,会先判断该⽅法上是否加了@Transactional注解 4. 如果加了,那么则利⽤事务管理器创建⼀个数据库连接 5. 并且修改数据库连接的autocommit属性为false,禁⽌此连接的⾃动提交,这是实现Spring事务⾮ 常重要的⼀步 6. 然后执⾏当前⽅法,⽅法中会执⾏sql 7. 执⾏完当前⽅法后,如果没有出现异常就直接提交事务 8. 如果出现了异常,并且这个异常是需要回滚的就会回滚事务
|
7月前
|
JavaScript Java 开发者
Spring事务失效,常见的情况有哪些?
本文总结了Spring事务失效的7种常见情况,包括未启用事务管理功能、方法非public类型、数据源未配置事务管理器、自身调用问题、异常类型错误、异常被吞以及业务和事务代码不在同一线程中。同时提供了两种快速定位事务相关Bug的方法:通过查看日志(设置为debug模式)或调试代码(在TransactionInterceptor的invoke方法中设置断点)。文章帮助开发者更好地理解和解决Spring事务中的问题。
238 7