第十八章《JDBC》第3节:事务处理
  TEZNKK3IfmPf 2023年11月14日 19 0

事务是数据库学科中非常重要的机制,它是保证底层数据完整的重要手段,没有事务支持的数据库都是非常脆弱的,本小节将讲解MySQL事务处理的基本技术和以及JDBC的事务支持方法。

18.3.1事务的概念和MySQL事务支持

事务是由一步或几步数据库操作序列组成的逻辑执行单元,这系列操作要么全部执行,要么全部放弃执行,一般而言,一段程序中可能包含多个事务。事务具备4个特性:原子性(Atomicity)、一致性(Consistency)、 隔离性(Isolation)和持续性(Durability),这4个特性也简称为ACID性。

  • 原子性(Atomicity):事务是应用中最小的执行单位,就如原子是自然界的最小颗粒,具有不可再分的特征一样,事务是应用中不可再分的最小逻辑执行体。
  • 一致性(Consistency):事务执行的结果,必须使数据库从一个一致性状态,变到另一个一致性状态。当数据库只包含事务成功提交的结果时,数据库处于一致性状态。如果系统运行发生中断,某个事务尚未完成而被迫中断,而该未完成的操作对数据库所做的修改已被写入数据库,此时数据库就处于一种不正确的状态。比如银行在两个账户之间转账:从A账户向B账户转入1000元,系统先减少A账户的1000元,然后再为B账户增加1000元。如果全部执行成功,数据库处于一致性状态,如果仅执行完A账户金额的修改,而没有增加B账户的金额,则数据库就处于不一致性状态。因此,一致性是通过原子性来保证的。
  • 隔离性(Isolation):各个事务的执行互不干扰,任意一个事务的内部操作对其他并发的事务都是隔离的。也就是说,并发执行的事务之间不能看到对方的中间状态,并发执行的事务之间不能互相影响。
  • 持续性(Durability):持续性也称为持久性(Persistence),指事务一旦提交, 对数据所做的任何改变都要记录到永久存储器中,通常就是保存进物理数据库。

通常情况下,数据库的事务有以下几种可能性:

  • 一组DML语句,经过这组DML语句修改后的数据将保持一致性。
  • 一条DDL语句。
  • 一条DCL语句。

当事务所包含的全部数据库操作都成功执行后,应该提交事务以使这些修改生效。事务提交有显式提交和自动提交两种方式。所有执行DDL或DCL语句都是自动提交的,在MySQL的默认情况下DML语句也是自动提交的,但程序员可以人为的设置DML为显式提交,也就是手动提交方式。

当事务所包含的任意一个数据库操作执行失败后,应该回滚事务,使该事务中所做的修改全部失效。事务回滚有也两种方式:显式回滚和自动回滚。显示回滚是调用rollback命令完成的,而自动回滚是在系统出错或强行退出的情况下完成的。

之前讲过,MySQL在默认情况下对DML语句是自动提交的,如果希望改成显式提交可以调用以下命令完成:

set autocommit=0​

如果程序员不想改变默认的事务自动提交方式,但又希望某一次执行语句时能够达到首动听提交的效果,可以使用MySQL提供的start transaction 或begin两个命令,它们都表示临时性地开始一次事务, 处于start transaction或begin后的DML语句不会立即生效,除非使用commit显式提交事务,例如下SQL代码将不会对数据库有任何影响。

begin;​
insert into users values ('12307','张三','男','1999-03-02','13800099000');​
insert into users values ('12308','李四','男','1999-06-12','13833445566');​
insert into users values ('12309','王五','男','1999-09-22','18823455432');​
rollback;​

之所以上面这段代码不会对数据库有任何影响,就是因为begin命令之后的DML语句已经不再是自动提交,必须调用commit命令显式提交,而以上代码的最末尾调用的是rollback命令完成回滚,这使得数据库又回到了原先的状态。需要注意:无论是提交还是回滚,都会使当前的事务结束。

除此之外,MySQL还提供了savepoint来设置事务的中间点,这个中间点相当于一个标记,程序员可以让事务回滚到指定的中间点,而不是回滚全部事务。下面的SQL语句设置了一个中间点a:

savepoint a;​

一旦设置了中间点后,就可以使用rollback回滚到指定中间点,回滚到指定中间点的代码如下:

rollback to a;​

需要注意:普通的提交、回滚都会结束当前事务,但回滚到指定中间点因为依然处于事务之中,所以不会结束当前事务。

18.3.2 JDBC的事务支持

JDBC连接也提供了事务支持,JDBC连接的事务支持由Connection提供,Connection 默认打开自动提交模式,在这种情况下,每条SQL语句一旦执行,便会立即提交到数据库,永久生效,无法对其进行回滚操作。程序员可以调用Connection的setAutoCommit()方法来关闭自动提交模式,代码如下:

con.setAutoCommit (false);​

相应的,Connection接口的getAutoCommit()方法可以返回该连接的自动提交模式。

一旦事务开始之后,程序可以像平常一样创建Statement对象,创建了Statement 对象之后,可以执行任意多条DML语句,如下代码所示: .

stm.executeUpdate(sql1);​
stm.executeUpdate(sql2);​
stm.executeUpdate(sql3);​

.上面这些SQL语句虽然被执行了,但这些SQL语句所做的修改不会生效,因为事务还没有结束。如果所有的SQL语句都执行成功,程序可以调用Connection的commit()方法来提交事务,代码如下:

con.commit() ;​

如果任意一条SQL语句执行失败,则应该用Connection的rollback()方法来回滚事务,代码如下:

con.rollback();​

实际上,当程序执行时出现一个未处理的SQLException 异常时,系统将会非正常退出,事务也会自动回滚。但如果程序捕获了该异常,则需要在异常处理块中显式地回滚事务。下面的【例18_07】展示了程序在出现没有捕获的异常时系统自动回滚的效果。

【例18_07 自动回滚事务】

Exam18_07.java

import java.sql.*;
public class Exam18_07 {
    public static void main(String[] args) throws  Exception{
        Connection con = Util1.getConneciton();//用Util1的方法获得Connection对象
        con.setAutoCommit(false);//关闭自动提交
        Statement stm = con.createStatement();
        stm.executeUpdate("insert games value(7,'五子棋','棋牌')");
        //下面这条语句无法执行成功并产生异常,因为主键冲突
        stm.executeUpdate("insert games value(7,'中国象棋','棋牌')");//①;
        con.commit();//提交事务
        Util1.close(null,stm,con);//释放资源
    }
}

【例18_07】中的Connection对象con被设置为关闭自动提交事务,程序中执行了两条插入数据的SQL语句,但第二条SQL语句因主键冲突不能执行成功,因此会产生异常,而main()方法并未捕获这个异常,这样事务会自动回滚,因而两条SQL语句都不会生效。在执行完本例后,读者查询games表会发现程序中试图插入的两条数据并未出现在数据库中。

Connection也提供了设置中间点的方法,如表18-6所示。

表18-6 Connection设置中间点的方法

方法 功能
Savepoint setSavepoint() 在当前事务中创建一个未命名的中间点,并返回代表该中间点的Savepoint对象
Savepoint setSavepoint(String name)
在当前事务中创建一个具有指定名称的中间点,并返回代表该中间点的Savepoint对象。

通常来说,设置中间点时没有太大的必要指定名称,因为Connection回滚到指定中间点时并不是根据名字回滚的,而是根据中间点对象回滚的,Connection 提供了rollback(Savepoint savepoint)方法回滚到指定中间点。

18.3.3批量更新

JDBC还提供了一个批量更新的功能,使用批量更新时,多条SQL语句将被作为一批操作被同时被执行。使用批量更新也需要先创建一个Staterment对象,然后利用该对象的addBatch()方法将多条SQL语句同时收集起来,最后调用executeLargeBatch()或executeBatch()方法同时执行这些SQL语句。只要批量操作中任何一条SQL语句影响的记录条数可能超过Integer.MAX_ VALUE,就应该使用executeLargeBatch(方法,而不是executeBatch()方法。下面的代码展示了如何执行批量更新。

Statement stm = con.createStatement() ;​
//使用Statement同时收集多条SQL语句​
stm.addBatch(sql1) ;​
stm.addBatch (sql2) ;​
stmt.addBatch(sql3) ;​
//同时执行所有的SQL语句​
stmt. executeLargeBatch();​

执行executeLargeBatch()方法将返回一个long[]数组,因为使用Statement执行DDL、DML语句都将返回一个long值,而执行多条DDL、DML语句将会返回多个long值,多个long值就组成了这个long[]数组。如果在批量更新的addBatch()方法中添加了select查询语句,程序将直接出现错误。

为了让批量操作可以正确地处理错误,必须把批量执行的操作视为单个事务,如果批量更新在执行过程中失败,则让事务回滚到批量操作开始之前的状态。为了达到这种效果,程序应该在开始批量操作之前先关闭自动提交,然后开始收集更新语句,当批量操作执行结束后,提交事务并恢复之前的自动提交模式。下面的【例18_08】展示了如何使用JDBC的批量更新。

【例18_08 批量更新】

Exam18_08.java

import java.sql.*;

public class Exam18_08 {
    public static void main(String[] args) {
        Connection con = null;
        Statement stm = null;
        try {
            con = Util1.getConneciton();//用Util1的方法获得Connection对象
            con.setAutoCommit(false);//关闭自动提交
            stm = con.createStatement();
            //一组SQL语句
            String[] sqls = {
                    "insert games value(7,'五子棋','棋牌')",
                    "insert games value(8,'中国象棋','棋牌')",
                    "insert games value(9,'消消乐','益智')"
            };
            //用循环的形式把要执行的SQL语句收集起来
            for (String sql : sqls) {
                stm.addBatch(sql);
            }
            //同时执行所有的SQL语句
            stm.executeLargeBatch();
            //提交修改
            con.commit();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                Util1.close(null,stm,con);//释放资源
            }catch (SQLException e){
                e.printStackTrace();
            }
        }
    }
}

执行完【例18_08】的案例代码后,读者再查询games表会看到表中新增加了3条数据,但再次执行这个程序会引发异常,这是因为产生了主键冲突。

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月14日 0

暂无评论

推荐阅读
  TEZNKK3IfmPf   2023年11月15日   52   0   0 JDBC
  TEZNKK3IfmPf   2023年11月14日   143   0   0 事务mysql
  TEZNKK3IfmPf   2024年04月19日   37   0   0 事务分布式
  TEZNKK3IfmPf   2023年11月14日   27   0   0 事务mysql
TEZNKK3IfmPf