• 10.8 事务处理


    一、事务的概念和MySQL事务支持

    1、概念:事务是由一步或几步数据库操作序列组成的逻辑单元,这系列的操作要么全部执行,要么全部放弃执行。程序和事务是两个不同的概念。一般而言,一段程序包含多个事务。

    2、事务的四个特性(简称为ACID性):原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持续性(Durability)。

    (1)原子性:事务是应用中最小的执行单位,就如原子是自然界最小颗粒,具有不可再分的特征一样,事务是应用中不可再分的最小逻辑执行体。

    (2)一致性事务执行的结果,必须使数据库从一个一致性状态,变到另一个一致性状态。当数据库只包含事务成功提交的结果时,数据库处于一致性状态。如果系统运行发生中断,某个事务尚未完成就被迫中断,而该未完成的事务对数据库所做的修改已被写入数据库。此时,数据库就处于一种不正确的状态。

    (3)隔离性:各个事务的执行互不干扰,任意一个事务的内部操作对其他并发的事务都是隔离的。也就是说,并发执行的事务之间不能看到对方的中间状态,并发执行的事务之间不能互相影响。

    (4)持续性:持续性也称为持久性,指事务一旦提交,对数据所做的任何改变都要记录到永久存储器中,通常就是保存进物理数据库。

    3、数据库的事务由下列语句组成:
    (1)一条DML语句,经过这组DML语句修改后的数据将保持较好的一致性。

    (2)一条DDL语句。

    (3)一条DCL语句。

    DDL和DCL语句最多只有一条,因为DDL和DCL语句都会导致事务立即提交。

    4、事务提交方式

    当事务所包含的全部数据库操作都执行后,应该提交(commit)事务,这些修改永久生效。

    ★显式提交:使用commit

    ★自动提交:执行DDL或DCL语句,或者程序正常退出。

    不管是显式提交还是隐式提交,都会结束当前事务。

    5、事务回滚

    当事务所包含的任意一个数据库操作执行失败后,应该回滚(rollback)事务,使事务中所做的修改全部失效。事务回滚有两种方式:

    ★显式回滚:使用rollback。

    ★自动回滚:系统错误或者强行退出。

    不管是显式回滚还是隐式回滚,都会结束当前事务。

    7、关闭自动提交、开启事务

    MySQL默认关闭事务(即打开自动提交),在默认情况下,用户在MySQL控制台输入一条DML语句,这条DML语句将会立即保存到数据库里。为了开启MySQL事务支持,可以显式调用如下命令:

    1 SET AUTOCOMMIT={0|1}   0为关闭自动提交,即开启事务

      提示:自动提交和事务开启刚好相反,如果开启自动提交就是关闭事务;关闭自动提交就是开启事务。

      一旦在MySQL命令行窗口输入SET AUTOCOMMIT=0开启事务,该命令行窗口里所有DML语句都不会立即生效,上一个事务结束后第一条DML语句将开始一个新的事务,而后续执行的所有SQL语句都处在该事务中,除非显式使用Commit来提交事务,或者正常退出,或者运行DDL、DCL语句导致事务隐式提交。当然也可以使用rollback回滚来结束事务,使用rollback结束事务将导致本次DML语句所做的修改全部失效。

       一个MySQL命令窗口代表一次连接Session,在该窗口设置SET AUTOCOMMIT=0相当于关闭该连接Session的自动提交,对其他连接不会有影响。

      如果不想关闭整个命令窗口的自动提交,而只是想暂时性地开始一次事务,则可以使用MySQL提供的star transaction或begin两个命令,它们都表示临时性地开始一次事务,处于star transaction或begin后的DML语句不会立即生效,除非使用commit显式提交事务,或者执行DDL、DCL语者来隐式提交事务。

    如下SQL语句不会对数据库产生任何影响:

     1 #临时开启事务
     2 begin;
     3 #向student_table1插入三条记录
     4 insert into student_table1
     5 values(null,'xx',1);
     6 insert into student_table1
     7 values(null,'yy',1);
     8 insert into student_table1
     9 values(null,'zz,1);
    10 #查询student_table1表的记录
    11 select *from student_table1;#①
    12 #回滚事务
    13 rollback;
    14 #再次查询
    15 select * from student_table1 ;#②

    上面的第①条查询语句将会看到刚刚插入的3条记录,如果打开MySQL的其他命令窗口将看不到这三条记录——这正体现了事务的隔离性。接着程序rollback了事务中的全部修改,执行第②条查询语句将看到数据库又恢复到了事务开始前的状态。

    不管是显式提交还是隐式提交,都会结束当前事务;不管是显式回滚还是隐式回滚,都会结束当前事务。

    除此之外,MySQL还提供了savepoint来设置事务的中间点,通过使用savepoint设置事务中间点还可以让事务回滚到指定中间点,而不是回滚全部事务。如下SQL语句设置了一个中间点:

    1 savapoint a;

    回滚到指定中间点:

    1 rollback a;

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

    二、JDBC事务支持

    JDBC也提供了事务支持,JDBC连接的事务支持由Connection提供,Connection默认打开自动提交,即关闭事务,在这种情况下,每条SQL语句一旦执行,并会立即提交到数据库中,永久有效,无法对其进行回滚操作。

    1、关闭自动提交,开启事务

    可以调用Connection的setAutoCommite()方法来关闭自动提交,开启事务,代码如下:

    1 //关闭自动提交,开启事务
    2 conn.setAutoCommit(false);

     2、返回连接的自动提交模式

    调用Connection提供的getAutoCommit()方法来返回该连接的自动提交模式。

    3、提交事务

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

    1 stmt.executeUpdate(...);
    2 stmt.executeUpdate(...);
    3 stmt.executeUpdate(...);

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

    1 //提交事务
    2 conn.commit();

    4、回滚事务

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

    1 #回滚事务
    2 conn.rollback();

    下面程序示范了当程序出现了未处理的SQLException异常时,系统将自动回滚事务。实际上,当Connection遇到一个未处理的SQLException异常时,系统会非常正常退出,事务也会自动回滚。但如果程序不活了该异常,则需要在程序中显式回滚事务。

     1 package section7;
     2 
     3 import java.io.FileInputStream;
     4 import java.sql.Connection;
     5 import java.sql.DriverManager;
     6 import java.sql.Statement;
     7 import java.util.Properties;
     8 
     9 public class TransactionTest
    10 {
    11     private  String  driver;
    12     private  String url;
    13     private String user;
    14     private String pass;
    15     public void initParam(String paramFile)
    16         throws Exception
    17     {
    18         //使用Properties类加载属性
    19         Properties props=new Properties();
    20         props.load(new FileInputStream(paramFile));
    21         driver=props.getProperty("driver");
    22         user=props.getProperty("user");
    23         url=props.getProperty("url");
    24         pass=props.getProperty("pass");
    25     }
    26     public void insertInTransaction(String[] sqls)
    27         throws Exception
    28     {
    29         //加载驱动
    30         Class.forName(driver);
    31         try(
    32                 Connection conn = DriverManager.getConnection(url,user,pass);
    33                 )
    34         {
    35             //关闭自动提交
    36             conn.setAutoCommit(false);
    37             try(
    38                     //使用Connection创建一个Statementd对象
    39                     Statement stmt=conn.createStatement()
    40                     )
    41             {
    42                 //循环多次执行SQL语句
    43                 for(var sql:sqls)
    44                 {
    45                     stmt.executeUpdate(sql);
    46 
    47                 }
    48             }
    49             //提交事务
    50             conn.commit();
    51         }
    52     }
    53     public static void main(String[] args)
    54         throws Exception
    55     {
    56         var tt=new TransactionTest();
    57         tt.initParam("src\mysql.ini");
    58         var sqls=new String[]{
    59                 "insert into student_table values(null,'aaa',1);",
    60                 "insert into student_table values(null,'bbb',1);",
    61                 "insert into student_table values(null,'ccc',1);",
    62                 //下面这条语句将会违反外键约束
    63                 //因为teacher_table表中没有ID为5的记录
    64                 "insert into student_table1 values(null,'aaa',5);"//
    65         };
    66         tt.insertInTransaction(sqls);
    67     }
    68 }
    View Code

    上面程序中执行到第4条sql语句时,这条语句将出现外键约束异常,该异常没有得到处理,引起程序的非正常结束,所事务自动回滚。

     Connection也提供了设置中间点的方法:

    1、Savepoint setSavePoint():在当前事务中创建一个未命名的中间点,并返回该中间点的Savepoint 对象。

     2、Savepoint setSavePoint(String name):在当前事务中创建一个指定名称的中间点,并返回该中间点的Savepoint 对象。(没有太大必要指定中间点名称)

    Connection提供rollback(Savepoint savepoint)方法回滚到指定中间点。

    三、使用批量更新

    JDBC还提供了一个批量更新功能,使用批量更新时,多条SQL语句将被作为一批操作被同时收集,并同时提交。

    提示:批量更新批量必须得到底层数据库的支持,可以通过调用MetabaseMetaData的supportsBatchUpdates()方法来查看底层数据库是否支持批量操作。

    批量跟新步骤:

    ①先创建一个Statement对象,然后利用该对象的addBatch()方法将多条SQL语句收集起来。

    ②最后调用Java 8为Statement对象新增的executeLargeBatch()(或原有的executeBatch())方法同时执行这些SQL语句。

    下面代码示范了如何执行这些批量更新:

    1 Statement stmt=conn.createStatement();
    2 //使用Statement同时收集多条SQL语句
    3 stmt.addBatch(sql1);
    4 stmt.addBatch(sql2);
    5 stmt.addBatch(sql3);
    6 ...
    7 //同时执行所有的SQL语句
    8 stmt.executeLargeBatch();

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

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

     1 package section7;
     2 
     3 import java.io.FileInputStream;
     4 import java.sql.Connection;
     5 import java.sql.Driver;
     6 import java.sql.DriverManager;
     7 import java.sql.Statement;
     8 import java.util.Properties;
     9 
    10 public class BatchTest
    11 {
    12     private  String  driver;
    13     private  String url;
    14     private String user;
    15     private String pass;
    16     public void initParam(String paramFile)
    17             throws Exception
    18     {
    19         //使用Properties类加载属性
    20         Properties props=new Properties();
    21         props.load(new FileInputStream(paramFile));
    22         driver=props.getProperty("driver");
    23         user=props.getProperty("user");
    24         url=props.getProperty("url");
    25         pass=props.getProperty("pass");
    26     }
    27     public void insertBatch(String[] sqls)
    28         throws Exception
    29     {
    30         //加载驱动
    31         Class.forName(driver);
    32         try(
    33                 Connection conn= DriverManager.getConnection(url,user,pass)
    34                 )
    35         {
    36             //保存当前的自动提交模式
    37             boolean autoCommit=conn.getAutoCommit();
    38             //关闭自动提交
    39             conn.setAutoCommit(false);
    40             try(
    41                     //使用Connection创建一个Statement对象
    42                     Statement stmt=conn.createStatement()
    43                     )
    44             {
    45                 //for循环多次执行SQL语句
    46                 for(var sql:sqls)
    47                 {
    48                     stmt.addBatch(sql);
    49                 }
    50                 //提交所有的SQL语句
    51                 stmt.executeLargeBatch();
    52                 //提交修改
    53                 conn.commit();
    54                 //恢复原有的自动提交模式
    55                 conn.setAutoCommit(autoCommit);
    56             }
    57         }
    58     }
    59     public static void main(String[] args)
    60         throws Exception
    61     {
    62         var bt=new BatchTest();
    63         bt.initParam("src\mysql.ini");
    64         var sqls=new String[]{
    65                 "insert into student_table values(null,'aaa',1)",
    66                 "insert into student_table values(null,'bbb',1)",
    67                 "insert into student_table values(null,'ccc',1)"
    68         };
    69         bt.insertBatch(sqls);
    70     }
    71 }
    View Code

  • 相关阅读:
    常见的行元素与块元素
    [转]SVN服务器部署并实现双机同步及禁止普通用户删除文件
    [转]Axure共享工程Shared Project(二):编辑修改和提交
    如何添加网络打印机
    [转]JSON 转换异常 死循环 There is a cycle in the hierarchy
    比较常用的Properties配置文件的使用方法示例
    解决Tomcat项目重复加载导致pemgen space内存溢出
    怎样批量删除.svn文件
    [转]前端工程师必须掌握的知识点
    Freemarker 使用
  • 原文地址:https://www.cnblogs.com/weststar/p/12709597.html
Copyright © 2020-2023  润新知