Java-JDBC-事务与设置回滚点

Java-JDBC-事务与设置回滚点

  • 事务能够保证SQL要么全部执行成功,要么全部执行失败
  • JDBC 默认是自动提交事务
    • 每条DML都是默认提交事务的,多个preparedStatement.executeUpdate();都会提交一次事务
  • 如果想手动控制事务,那么就不能让事务自动提交
  • 通过Connection对象控制connection.setAutoCommit(false)不自动提交事务;
  • 如果不设置 默认值为true,自动提交,设置为false之后就是手动提交了
  • 无论是否发生回滚,事务最终会一定要提交的 提交我们建议放在finally之中进行提交
  • 如果是转账的过程中出现异常了,那么我们就要执行回滚,回滚操作应该方法catch语句块中

事务示例

package com.shanlei.test05;

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

/**
 * @author: shanlei
 * @version: 1.0
 */
public class TestTransaction {
    private static String url = "jdbc:mysql://localhost/mytestdb?useSSL=false&useUnicode=true&charcterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useServerPrepStmts=true&cachePrepStmts=true&rewriteBatchedStatements=true";
    private  static String user = "root";
    private static String password ="123456";
    private static String driver = "com.mysql.cj.jdbc.Driver";
    // 这是main方法,实现程序主要逻辑
    public static void main(String[] args) {
        testTransaction();

    }
    public static void testTransaction(){
        /*
        使用AddBatch实现批处理
         */
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        /*
         * JDBC 默认是自动提交事务
         * 每条DML都是默认提交事务的,多个preparedStatement.executeUpdate();都会提交一次事务
         * 如果想手动控制事务,那么就不能让事务自动提交
         * 通过Connection对象控制connection.setAutoCommit(false);
         * 如果不设置 默认值为true,自动提交,设置为false之后就是手动提交了
         * 无论是否发生回滚,事务最终会一定要提交的 提交我们建议放在finally之中进行提交
         * 如果是转账的过程中出现异常了,那么我们就要执行回滚,回滚操作应该方法catch语句块中
          */
        try {
            //注册驱动
            Class.forName(driver);

            // 获取连接
            connection = DriverManager.getConnection(url, user, password);
            // connection控制不自动提交
            connection.setAutoCommit(false);

            // 准备SQL并添加到队列中
            String sql = "update account set balance = balance -? where id = ?;";
            preparedStatement = connection.prepareStatement(sql);
            // 转出
            preparedStatement.setDouble(1,100);
            preparedStatement.setDouble(2,1);
            preparedStatement.executeUpdate();
            // 转入
            preparedStatement.setDouble(1,-100);
            preparedStatement.setDouble(2,2);
            preparedStatement.executeUpdate();

        } catch (ClassNotFoundException | SQLException e) {
            // 语句执行异常,在catch中进行回滚事务
            if(null != connection){
                try {
                    connection.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            }

            e.printStackTrace();
        } finally {
            // 在关闭资源之前进行提交,无论是否执行回滚,都需要进行提交,所以将提交放在finally里面
            if(null != connection){
                try {
                    connection.commit();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

            if(null!=preparedStatement){
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(null!=preparedStatement){
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

回滚点

  • 在进行大批量操作的时候,可能由于最后几条数据有问题导致出现异常,直接回滚会导致前面的大量数据丢失
  • JDBC中可以设置回滚点,指定回滚操作需要回滚到哪个回滚点
  • 这样可以保证即使程序出现异常,也有部分数据能够被正常保存下来,减少数据丢失
  • 使用connection.setSavepoint()方法获取回滚点
  • 在回滚的时候connection.rollback(sp);方法中传入回滚点可以设置回滚到哪个回滚点
package com.shanlei.test05;

import java.sql.*;
import java.util.LinkedList;

/**
 * @author: shanlei
 * @version: 1.0
 */
public class TestTransaction2 {
    private static String url = "jdbc:mysql://localhost/mytestdb?useSSL=false&useUnicode=true&charcterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useServerPrepStmts=true&cachePrepStmts=true&rewriteBatchedStatements=true";
    private  static String user = "root";
    private static String password ="123456";
    private static String driver = "com.mysql.cj.jdbc.Driver";
    // 这是main方法,实现程序主要逻辑
    public static void main(String[] args) {
        testAddBatch();

    }
    public static void testAddBatch(){
        /*
        设置回滚点,实现大批量操作的时候,就算出错也能保证部分数据成功执行
         */
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        // 定义一个savePrint 的LinkedList,用来存放每一个回滚点
        LinkedList<Savepoint> linkedList = new LinkedList<>();

        try {
            //注册驱动
            Class.forName(driver);
            // 获取连接
            connection = DriverManager.getConnection(url, user, password);
            // 准备SQL并添加到队列中
            String sql = "insert into dept values (default,?,?)";
            preparedStatement = connection.prepareStatement(sql);
            for (int i = 0; i < 100856; i++) {
                preparedStatement.setString(1,"成都分部");
                preparedStatement.setString(2,"chengdu");
                preparedStatement.addBatch();  // 每循环一次就将一句sql语句放入批次中
                if(i%1000 == 0) {// 每当在批次中设置了1000条数据后,就开始执行一次并设置回滚点
                    // 执行SQL,设置回滚点
                    preparedStatement.executeBatch();//执行批处理中的语句
                    preparedStatement.clearBatch();// 清理批处理中的数据
                    linkedList.add(connection.setSavepoint()); // 设置回滚点,并将回滚点放到集合中
                }
            }
        } catch (ClassNotFoundException | SQLException e) {
            // 语句执行异常,在catch中进行回滚事务
            if(null != connection){
                try {
                    // 获取最后回滚点,用于指定到具体回滚点
                    Savepoint sp = linkedList.getLast();
                    if(null != sp){ // 如果指定的回滚点不等于null,那么久回滚到回滚点,否则就全部回滚
                        connection.rollback(sp);
                    }else{
                        connection.rollback();
                    }
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            }
            e.printStackTrace();
        } finally {
            // 在关闭资源之前进行提交,无论是否执行回滚,都需要进行提交,所以将提交放在finally里面
            if(null != connection){
                try {
                    connection.commit();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(null!=preparedStatement){
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(null!=preparedStatement){
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}


posted @ 2021-01-31 19:05  殃奕  阅读(848)  评论(0编辑  收藏  举报