事务回滚

  • 理解:防止出现未知错误,导致原先要执行完全的数据只执行了一半,最终影响数据,也就是 事务是一组组合成逻辑工作单元的操作,虽然系统中可能会出错,但事务将控制和维护事务中每个操作的一致性完整性
  • 事务遵循ACID原则
  1. 原子性:要么全部完成,要么都不完成
  2. 一致性:总数不变
  3. 隔离性:多个进程互不干扰
  4. 持久性:一旦提交不可逆,即持久化到数据库
  • 下面在实例中体现事务回滚的作用,假设现在有个转账业务
  • 且有如下的用户信息:
  • 事务手动回滚 java java事务回滚原理_jdbc

  • 当我们想要对这个表进行如下操作:
  1. stu_id=1的用户stu_salary+1000
  2. stu_id=3的用户stu_salary-1000

正常执行的code:

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;
        PreparedStatement ps= null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test1?useSSL=true","root","root");
            String sql1="UPDATE student SET stu_salary=stu_salary+1000 WHERE stu_id=1";
            ps = conn.prepareStatement(sql1);
            ps.executeUpdate();
            String sql2="UPDATE student SET stu_salary=stu_salary-1000 WHERE stu_id=3";
            ps = conn.prepareStatement(sql2);
            ps.executeUpdate();
            System.out.println("执行成功");
        } catch (SQLException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        finally {
            //资源释放
            try {
                ps.close();
                conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
}
结果:

事务手动回滚 java java事务回滚原理_jdbc_02


注:很成功!

第一个执行第二个未执行的code:

  • 假设有未知原因这里将未知原因显式的定义成:int x=1/0;从而导致第一个执行,第二个尚未执行的code:
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");
            String sql1="UPDATE student SET stu_salary=stu_salary+1000 WHERE stu_id=1";
            ps = conn.prepareStatement(sql1);
            ps.executeUpdate();
            int x=1/0;
            String sql2="UPDATE student SET stu_salary=stu_salary-1000 WHERE stu_id=3";
            ps = conn.prepareStatement(sql2);
            ps.executeUpdate();
            System.out.println("执行成功");
        } catch (SQLException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        finally {
            //资源释放
            try {
                ps.close();
                conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
}
结果::

事务手动回滚 java java事务回滚原理_事务手动回滚 java_03


注:成功执行了第一个sql语句,但是第二个由于int x=1/0;的错误而没有执行,显然,这样是不规范的。那么就有了下面的事务回滚

  • 利用事务回滚,实现事务的原子性

两个一起执行,或者一起不执行的code:

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;
            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();
            }
        }
    }
}
结果:

事务手动回滚 java java事务回滚原理_jdbc_04

  • 解释:
    1. 因为数据库是自动提交的(就是执行code后读取到哪执行到哪,这也是要进行事务回滚的原因),所以我们要将数据库的自动提交关闭,那么使用codeconn.setAutoCommit(false); 2. 关闭了自动提交,但总是要提交事务的,由该codeconn.commit();实现
    3. 上面两个设置了基本就可以实现原子性,这里为了防止意外,在catch语句中再次显式的用:conn.rollback();实现事物的回滚