JDBC学习(六) --- 事务管理

JDBC 事务管理

 数据库事务是由若干个 SQL 语句构成的一个操作序列。数据库系统保证在一个事务中的所有 SQL 语句要么全部执行成功,要么全部不执行,即数据库事务具有 ACID 特性:

  • Atomicity: 原子性
  • Consistency: 一致性
  • Isolation: 隔离性
  • Durability: 持久性

 数据库事务可以并发执行,而数据库系统从效率考虑,对事务定义了不同的隔离级别。SQL 标准定义了 4 种隔离级别,分别对应可能出现的数据不一致的情况:

Isolation Level脏读(Dirty Read)不可重复读(Non Repeatable Read)幻读(Phantom Read)
Read UncommittedYesYesYes
Read Committed-YesYes
Repeatable Read--Yes
Serializable---

数据库事务的必要性

 数据库事务的一个经典案例就是银行转账操作。现假定小明要给小红转账 100 元,两人的银行账户(accountId,假定为 Primary Key)分别 123,456。那么银行那边应该执行的 SQL 语句如下:

-- 小明账户的操作
UPDATE accounts SET balance = balance - 100 WHERE id = 123 AND balance >= 100;

-- 小红账户的操作
UPDATE accounts SET balance = balance + 100 WHERE id = 456;

 假定银行后台在处理完小明账户的操作后,突然发生了不可抗拒的因素使得小红账户的操作无法执行,那么系统上的总额就会凭空少了 100 元。而有了事务后,这两个操作要么执行成功,要么都不执行,保证了系统上的总额不变。

 数据库事务不是本文的重点,本文的重点是如何通过 JDBC 进行事务管理。

JDBC 的事务管理

 事务的本质就是一些列的 SQL 操作序列,因此要在 JDBC 中执行事务,本质上就是如何把多条 SQL 语句包裹在一个数据库事务中执行。JDBC 的事务处理相对固定,如下:

Connection conn = null;
try {
    conn = JDBCUtils.getMyConnection();
    // 关闭自动提交
    conn.setAutoCommit(false);
    // 执行多条SQL语句
    insert();
    update();
    delete();
    // 提交事务
    conn.commit();
}catch (Exception e){
    // 执行过程中出错,则回滚事务
    conn.rollback();
}finally{
    conn.setAutoCommit(true);
}

 其中,开启事务的关键代码是conn.setAutoCommit(false),表示关闭自动提交。提交事务的代码在执行完指定的若干条 SQL 语句后,调用conn.commit()。要注意事务不是总能成功,如果事务提交失败,会抛出 SQL 异常(也可能在执行 SQL 语句的时候就抛出了),此时我们必须捕获并调用conn.rollback()回滚事务。最后,在 finally 中通过conn.setAutoCommit(true)Connection对象的状态恢复到初始值。

 下面的代码就是事务处理的一个例子:

@Test
public void transactionTest(){
    /**
     * 向order表中添加一条order_name为JDBC的数据
     */
    Connection conn = null;
    PreparedStatement preparedStatement = null;
    ResultSet resultSet = null;
    try{
        conn = JDBCUtils.getJDBCConnection();
        // 事务开始
        conn.setAutoCommit(false);
        String sql = "INSERT INTO `order`  VALUES (4,'JDBC');";
        Statement statement = conn.createStatement();
        int i = statement.executeUpdate(sql);
        // 事务提交
        conn.commit();
        System.out.println("插入成功");
    }catch (Exception e){
        try {
            // 事务回滚
            conn.rollback();
            // 恢复
            conn.setAutoCommit(true);
        }catch (Exception setE){
            setE.printStackTrace();
        }
        e.printStackTrace();
    }finally {
        try {
            JDBCUtils.closeResource(conn,preparedStatement,resultSet);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

 代码执行后,我们可以正常的插入一条数据,现在我们使用下一段代码来演示当我们可以成功插入语句时,在事务提交前出现了异常后的事务回滚:

@Test
public void transactionTest(){
    /**
     * 向order表中添加一条order_name为JDBC的数据
     */
    Connection conn = null;
    PreparedStatement preparedStatement = null;
    ResultSet resultSet = null;
    try{
        conn = JDBCUtils.getJDBCConnection();
        // 事务开始
        conn.setAutoCommit(false);
        String sql = "INSERT INTO `order`  VALUES (6,'Python');";
        Statement statement = conn.createStatement();
        int i = statement.executeUpdate(sql);
        System.out.println("成功修改了" + i + "条记录");
        // 当成功修改记录时,主动抛出一个异常,i的值表示的是修改了i条记录
        if(i > 0){
            System.out.println("出现异常!!!");
            throw new Exception();
        }
        // 事务提交
        conn.commit();
        System.out.println("插入成功");
    }catch (Exception e){
        try {
            // 事务回滚
            conn.rollback();
            conn.setAutoCommit(true);
            System.out.println("更新失败!");
        }catch (Exception setE){
            setE.printStackTrace();
        }
        e.printStackTrace();
    }finally {
        try {
            JDBCUtils.closeResource(conn,preparedStatement,resultSet);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

控制台信息:

成功修改了1条记录
出现异常!!!
更新失败!

order 表信息:

可以发现 尽管插入操作执行成功,但因为后序出现了异常,导致我们的插入操作失效。

还原点回滚技术

 大部分现代的数据库管理系统的环境都支持设定还原点,例如 Oracle 的 PL/SQL。当你在事务中设置一个还原点来定义一个逻辑回滚点后,如果在一个还原点之后发生错误,那么可以使用 rollback 方法来撤消所有的修改或在该还原点之后所做的修改

 Connection 对象有两个新的方法来管理还原点:

  • setSavepoint(String savepointName):定义了一个新的还原点,返回一个 Savepoint 对象。
  • releaseSavepoint(Savepoint savepointName):删除一个还原点。它需要一个作为参数的 Savepoint 对象,这个对象通常是由 setSavepoint()方法生成的一个还原点。

 还原点的使用其实与事务回滚的使用类似,如下:

try{
   /
   conn.setAutoCommit(false);
   Statement stmt = conn.createStatement();

   //设置一个名为“Savepoint1”还原点
   Savepoint savepoint1 = conn.setSavepoint("Savepoint1");
   // 一条正确的SQL语句
   String SQL = "INSERT INTO Employees " +
                "VALUES (106, 20, 'Rita', 'Tez')";
   stmt.executeUpdate(SQL);
   //错误的SQL语句
   String SQL = "INSERTED IN Employees " +
                "VALUES (107, 22, 'Sita', 'Tez')";
   stmt.executeUpdate(SQL);
   // 提交
   conn.commit();

}catch(SQLException se){
   // 回滚到还原点Savepoint1
   conn.rollback(savepoint1);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PG Thinker

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值