02JDBC事务支持

此文转载自:https://blog.csdn.net/qq_43625140/article/details/110084389

1. 数据库事务

1.1 是什么

什么是事务,可以阅读我写的有关数据库 事务 这篇文章。从事务的四大特性来说,事务机制是为了保证数据的一致性,事务是最小的执行单元,要么全部成功要么全部不成功。

1.2 为什么

众所周知,SQL语言是一种操作数据库的非过程化语言,无疑它的功能是很强大的。比如一个小小的select语法结构就可以查询到我们想要的任何数据,多么简洁方便。

然而业务是复杂的,一条SQL并不能满足我们的需求,往往一个业务的执行需要多条SQL语句的配合使用来完成。一个写操作会改变数据库的数据,多个写操作如果不能保证都成功那么数据必然不是完整的。

为了保证数据的完整性就必须保证多条SQL语句的执行要么都成功要么都不成功,因此引出数据库事务机制。事务能够保证我们在解决复杂业务时,某一步出错不会造成数据的丢失或者增加。

转账案例

  • 例如现在有李白和杜甫两个人的存款在数据库中存在记录,他们各有500元的存款如下:

在这里插入图片描述

  • 李白要向杜甫转账200元,那我们必须修改两个人的存款,李白存款减200,杜甫存款加200,SQL如下:
# 李白存款减200
update `user` set money = money - 200 where name = '李白';

# 杜甫存款加200
update `user` set money = money + 200 where name = '杜甫';
  • 针对这个转账业务我们需要使用两条更新语句来完成,然而我们的程序只能先执行完一条再执行另一条,也就是说程序执行需要一个过程。
  • 如果在执行 李白存款减200后,杜甫存款加200前这个极其短暂的时间空隙时出现了故障,导致我们只执行了减款操作而没有执行加款操作,那么200元岂不是不翼而飞。

在这里插入图片描述

  • 李白和杜甫就不会打起来吗?李白说:“我转了,因为我的存款少了200元”,杜甫说:“你没转,因为我的存款没有多200元”。系统无缘无故吞了200元,现在谁还敢存钱。
  • 问题在于两条SQL语句我们只成功执行了一条SQL语句,导致数据库的金额数据总量少了200元,这样直接影响了数据的一致性。为了避免出现这样的情况,引入事务机制,将多条SQL语句加持在一个最小执行单元中,执行成功则业务成功,执行失败就貌似我们没有执行过,数据不会发生丢失或增加。

2. JDBC事务支持

JDBC作为java连接数据库的API自然也提供了相关方法来操作事务:

  • connection.setTransactionIsolation(int level):设置事务隔离级别

  • connection.setAutoCommit(boolean ):设置是否自动提交事务,默认自动提交,类似于是否开启事务。

  • connection.setSavepoint(String):设置保存点

  • connection.commit():提交事务

  • connection.rollback():回滚事务,默认回滚到最初,也可以指定回滚到指定保存点。

3. JDBC事务案例

基于李白向杜甫转账的例子,我们通过JDBC来进行操作,探究事务是否可以被控制住。

3.1 数据库环境

# 新建用户表
CREATE TABLE IF NOT EXISTS `user`(
   `pk_id` INT AUTO_INCREMENT,
   `name` VARCHAR(20) NOT NULL,
   `money` DOUBLE,
   CONSTRAINT `pk_user` PRIMARY KEY(`pk_id`)
);

# 添加用户数据
INSERT INTO 
  `user`(`name`, `money`)
VALUES
  ('李白', 500.0),
  ('杜甫', 500.0);

在这里插入图片描述

3.2 创建项目

  • 通过IDEA基于Maven构建普通Java工程

在这里插入图片描述

3.3 添加依赖

<!-- 依赖管理 -->
<dependencies>
    <!-- mysql -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.49</version>
    </dependency>
    <!-- junit -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.1</version>
    </dependency>
</dependencies>

3.4 转账异常

@Test
public void test01() throws ClassNotFoundException, SQLException
{
    // 定义连接参数
    String driverName = "com.mysql.jdbc.Driver";
    String url = "jdbc:mysql://127.0.0.1:3306/zw_test";
    String username = "root";
    String password = "root";

    // 1.注册驱动
    Class.forName(driverName);

    // 2.获取连接
    Connection connection = DriverManager.getConnection(url, username, password);

    // 3.增删改查
    Statement statement = connection.createStatement();

    // 李白扣钱
    String sql1 = "update user set money = money - 200 where name = '李白'";
    statement.executeUpdate(sql1);

    // 模拟发生意外
    int i = 1/0;

    // 杜甫加钱
    String sql2 = "update user set money = money + 200 where name = '杜甫'";
    statement.executeUpdate(sql2);

    // 4.关闭资源
    statement.close();
    connection.close();
}
  • 转账前

在这里插入图片描述

  • 转账后

在这里插入图片描述

3.5 转账正常

@Test
public void test02() throws ClassNotFoundException, SQLException
{
    // 定义连接参数
    String driverName = "com.mysql.jdbc.Driver";
    String url = "jdbc:mysql://127.0.0.1:3306/zw_test";
    String username = "root";
    String password = "root";

    // 1.注册驱动
    Class.forName(driverName);

    Connection connection = null;
    Statement statement = null;

    try
    {
        // 2.获取连接
        connection = DriverManager.getConnection(url, username, password);

        // 3.增删改查
        
        // 关闭事务的自动提交
        connection.setAutoCommit(false);
        
        // 获取操作对象
        statement = connection.createStatement();

        // 李白扣钱
        String sql1 = "update user set money = money - 200 where name = '李白'";
        statement.executeUpdate(sql1);

        // 模拟发生意外
        int i = 1/0;

        // 杜甫加钱
        String sql2 = "update user set money = money + 200 where name = '杜甫'";
        statement.executeUpdate(sql2);
        
        // 提交事务
        connection.commit();
    }
    catch (Exception e)
    {
        e.printStackTrace();
        
        // 发生异常,回滚事务
        connection.rollback();
    }
    finally
    {
        // 4.关闭资源
        statement.close();
        connection.close();
    }
}
  • 转账前

在这里插入图片描述

  • 转账后

在这里插入图片描述

原文地址:https://www.cnblogs.com/phyger/p/14035140.html