JDBC中事务回滚

2023-02-02 10:58:41 浏览数 (2)

JDBC中事务回滚

首先,什么是 事务回滚

 理解:防止出现未知错误,导致原先要执行完全的数据只执行了一半,最终影响数据,也就是 事务是一组组合成逻辑工作单元的操作,虽然系统中可能会出错,但事务将控制和维护事务中每个操作的一致性和完整性。

事务遵循ACID原则:

原子性:要么全部完成,要么都不完成 一致性:总数不变 隔离性:多个进程互不干扰 持久性:一旦提交不可逆,即持久化到数据库

事务回滚作用

 假设现在有一个业务逻辑是 张三 给 李四 转账,张三在银行A发起了转账操作(此处我们假设银行使用mysql进行数据存储),此时数据库需要完成两个操作,第一个操作是从张三的余额当中扣除对应的金额,第二个操作是给李四的账户余额中增加余额。

 突然有一天,出现了一个bug,在张三转账的时候,系统扣除了张三的余额后,系统突然故障,无法正常工作,崩溃了。但是这个时候李四的余额还没增加,这个时候这笔余额就无缘无故消失了。

 这就增加了问题严重性。这个时候,事务回滚就是用来防止这种情况的发生的,事务回滚在这个场景的用处简单来说就是 将张三扣除余额和李四增加余额,这两个操作绑定在一次,同时操作,这样就避免了当张三扣除余额后,李四的余额每增加这种问题的发生。

JDBC事务回滚的语法说明

基础语法: conn.setAutoCommit(boolean) :设置是否为自动提交事务,如果true(默认值为true)表示自动提交,也就是每条执行的SQL语句都是一个单独的事务,如果设置为false,那么相当于开启了事务了;conn.setAutoCommit(false) 表示开启事务。 conn.commit() :提交结束事务。(将所有操作在这一步一起执行) conn.rollback() :回滚事务。(返回到数据初始的章台)

示例代码(来自 csdn文章 )

代码语言:javascript复制
package com.mystudy.jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class Affair {
    public static void main(String[] args) {
        Connection conn= null;
        int a = 0,b=0;
        PreparedStatement ps= null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test1?useSSL=true","root","root");
            //关闭数据库的自动提交
            conn.setAutoCommit(false);
            String sql1="UPDATE student SET stu_salary=stu_salary 1000 WHERE stu_id=1";
            ps = conn.prepareStatement(sql1);
            ps.executeUpdate();
            int x=1/0; //此处是制造bug,使程序中断
            String sql2="UPDATE student SET stu_salary=stu_salary-1000 WHERE stu_id=3";
            ps = conn.prepareStatement(sql2);
            ps.executeUpdate();
            //业务完毕,提交事务
            conn.commit();
            System.out.println("执行成功");
        } catch (SQLException | ClassNotFoundException e) {
            try {
                //如果失败则默认回滚,
                conn.rollback();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
            e.printStackTrace();
        }
        finally {
            //资源释放
            try {
                ps.close();
                conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
}

JDBC Savepoint 创建检查点

 有时候一个事务可能是一组复杂的语句,因此可能想要回滚到事务中某个特殊的点。JDBC Savepoint帮我们在事务中创建检查点(checkpoint),这样就可以回滚到指定点。当事务提交或者整个事务回滚后,为事务产生的任何保存点都会自动释放并变为无效。把事务回滚到一个保存点,会使其他所有保存点自动释放并变为无效。

1.在事务中创建检查点

2.异常中捕捉检查点并回滚到检查点

代码语言:javascript复制
savepoint = conn.setSavepoint("检查点");
//设置检查点
conn.rollback(savepoint);
//回顾到指定的检查点

 示例代码(来自 csdn文章 )

代码语言:javascript复制
import cn.hutool.core.util.StrUtil;
import java.sql.*;
 
public class Jdbc {
 
    public static final String URL = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC";
    public static final String USER = "root";
    public static final String PASSWORD = "123456";
 
 
    public static void main(String[] args) throws Exception {
        //1.加载驱动程序
        Class.forName("com.mysql.cj.jdbc.Driver");
        //2. 获得数据库连接
        Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
        //3.操作数据库,实现增删改查
        String classSql = "insert into class (id, teacher_id) values (?,?)";
        String userSql = "insert into users (name, age) values (?,?)";
        Savepoint savepoint = null;
 
        try {
            //设置事务自动提交为手动提交
            conn.setAutoCommit(false);
            PreparedStatement preparedStatement1 = conn.prepareStatement(classSql);
            for (int i = 20; i < 30; i  ) {
                preparedStatement1.setObject(1, i);
                preparedStatement1.setObject(2, i);
                preparedStatement1.addBatch();
            }
            int [] success = preparedStatement1.executeBatch();
            System.out.println(success.length);
            //设置回滚的点  失败只会回滚users信息
            savepoint = conn.setSavepoint("检查点");
            //手动制造异常
            int m = 10 / 0;
            PreparedStatement preparedStatement2 = conn.prepareStatement(userSql);
            preparedStatement2.setObject(1, "腕豪");
            preparedStatement2.setObject(2, "24");
            preparedStatement2.executeUpdate();
            //提交事务
            conn.commit();
 
        }  catch (Exception e) {
            e.printStackTrace();
            if (StrUtil.isEmptyIfStr(savepoint)) {
              //此处判断savepoint检查点是否为空,如果是空,就回滚整个事务  
              //回滚所有更新sql
                conn.rollback();
                System.out.println("JDBC Transaction rolled back successfully");
            } else {
              //如果不为空,即代表设置了检查点
                //回滚到指定位置
                conn.rollback(savepoint);
                System.out.println("JDBC Transaction rolled back successfully");
              //提交检查点之前设置的事务
                conn.commit();
            }
 
        } finally {
            //执行完数据库操作后记得关闭数据库连接资源
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
 
        }
 
    }
}

本文共 938 个字数,平均阅读时长 ≈ 3分钟

0 人点赞