• 使用JDBC进行数据库的事务操作(2)


      本篇将讲诉如何使用JDBC进行数据库有关事务的操作。在上一篇博客中已经介绍了事务的概念,和在MySQL命令行窗口进行开启事务,提交事务以及回滚事务的操作。

      似乎事务和批处理都可以一次同时执行多条SQL命令,但是事务是如果某一条SQL出错,则前面已经执行过的SQL全部都将回滚;而批处理中某一条SQL出错,那么这条出错的SQL要么会抛出个异常,要么以一个代表出错的值返回,已经执行过的SQL不受影响,至于后面的SQL是否还会执行则看数据库,不同数据库有不同的处理。

      在前一篇我们说过,数据库对于事务是默认自动提交的,也就是发一条SQL命令则数据库就执行一条。而对于JDBC而言,当向数据库获取一个链接Connection对象,在默认情况下通过Connection对象发送的SQL命令也是默认自动提交事务的。

    操作:

      ① 如果我们想使用JDBC对多条SQL进行整体执行,需要先提交事务命令,这一步是通过Connection对象先将自动提交关闭,调用Connection对象的setAutoCommit(false)方法即可。这个方法相当于在MySQL命令行窗口中输入”start transaction”命令。

      ② 如果我们在JDBC已经将自动提交关闭的情况下需要提交事务,则调用Connection对象的commit()方法即可。

      ③ 如果我们在JDBC已经将自动提交关闭的情况下需要回滚事务,则调用Connection对象的rollback(…)方法即可。rollback方法如果是无参,则回滚前面所有已执行的SQL命令;如果是有参,则可以指定回滚点,保留前面部分指定的已执行的SQL命令。

      下面以几个案例来像上一篇博客一样分别介绍几种事务的相关操作。

    例1:正确提交事务的小案例

      在这个简单的案例中,通过用户A向用户B转账100元来快速了解如何使用JDBC操作事务。

    创建数据库和表,另外再添加两条数据:

        create database jdbcdemo;
    
        use jdbcdemo;
    
        create table account(
           id int primary key auto_increment,
          name varchar(40),
          money double
        );
    
        insert into account(name,money) values('a',1000);
        insert into account(name,money) values('b',1000);

    先看看准备的数据:

      

    创建工程,在工程中导入数据库连接驱动的jar包。在【src】目录下新建一个database.properties文件,内容如下:

        driver=com.mysql.jdbc.Driver
        url=jdbc:mysql://localhost:3306/jdbcdemo
        username=root
        password=root

    构建JDBC的工具类,包括注册驱动,获取连接,释放资源和连接等,这部分同《JDBC操作数据库的学习(2)》中相同,此处略。

    使用JDBC完成事务操作的demo代码如下:

     1 public void transaction() throws SQLException {
     2         Connection conn = null;
     3         PreparedStatement st = null;
     4         ResultSet rs = null;
     5         try{
     6             conn = JdbcUtils.getConnection();
     7             conn.setAutoCommit(false);  //开启事务,相当于start transaction命令
     8             
     9             String sql1 = "update account set money=money-100 where name='a'";
    10             st = conn.prepareStatement(sql1);
    11             st.executeUpdate();     //执行SQL语句
    12             
    13             String sql2 = "update account set money=money+100 where name='b'";
    14             st = conn.prepareStatement(sql2);
    15             st.executeUpdate();    //执行SQL语句
    16             
    17             conn.commit();   //提交事务
    18         }finally{
    19             JdbcUtils.release(conn, st, rs);
    20         }
    21     }
    View Code

    查看事务执行情况:

      

    通过例1中简单的代码就完成了事务的一系列操作。可以看到用户A向用户B确实转账了100元。

    例2:事务执行过程出错回滚的小案例

      通过前一篇博客,我们知道如果在事务执行过程中发生了错误,则数据库将会使该事务中所有的操作都回滚,那么我们在使用JDBC的情况下也来重新模拟一次,依然还是用户A向用户B转账100元。

      将例1中的account表所有用户的金额重新制定为1000元的SQL命令:

        update account set money=1000;

      

    创建工程,在工程中导入数据库连接驱动的jar包。在【src】目录下新建一个database.properties文件,内容如下:

        driver=com.mysql.jdbc.Driver
        url=jdbc:mysql://localhost:3306/jdbcdemo
        username=root
        password=root

    构建JDBC的工具类,包括注册驱动,获取连接,释放资源和连接等,这部分同《JDBC操作数据库的学习(2)》中相同,此处略。

    在使用JDBC进行事务处理中,我们添加一个显而易见的错误:int x = 1/0 ,如下代码:

     1 public void transaction() throws SQLException {
     2         Connection conn = null;
     3         PreparedStatement st = null;
     4         ResultSet rs = null;
     5         try{
     6             conn = JdbcUtils.getConnection();
     7             conn.setAutoCommit(false);  //开启事务,相当于start transaction命令
     8             
     9             String sql1 = "update account set money=money-100 where name='a'";
    10             st = conn.prepareStatement(sql1);
    11             st.executeUpdate();
    12             
    13             int x = 1/0;      //在此处模拟事务处理过程中出错
    14             
    15             String sql2 = "update account set money=money+100 where name='b'";
    16             st = conn.prepareStatement(sql2);
    17             st.executeUpdate();
    18             
    19             conn.commit();   //提交事务
    20         }finally{
    21             JdbcUtils.release(conn, st, rs);
    22         }
    23     }
    View Code

      当我们执行这个Java方法时,由于设置了int x = 1/0这个逻辑错误,程序会抛出异常,但是因为抛出异常后,后面的代码不再执行,也就是说程序无法执行到提交事务conn.commit()方法处,因此数据库将会回滚该事务所有的操作,因此A与B的金额还是原来那样:

      

    例3:事务执行过程出错由开发者手动回滚的小案例

      在例2中当事务执行过程中出错时,会由数据库自动回滚所有的操作,而在JDBC中,我们也可以调用链接Connection对象的rollback()方法回滚所有的操作,在下面的案例中,我们将在捕获异常的代码块中手动回滚所有的操作。

      所有表、表中数据、配置文件和JDBC工具类都同例2相同。

      将例2中的代码修改为如下:

     1 public void transaction() throws SQLException {
     2         Connection conn = null;
     3         PreparedStatement st = null;
     4         ResultSet rs = null;
     5         try{
     6             conn = JdbcUtils.getConnection();
     7             conn.setAutoCommit(false);  //开启事务,相当于start transaction命令
     8             
     9             String sql1 = "update account set money=money-100 where name='a'";
    10             st = conn.prepareStatement(sql1);
    11             st.executeUpdate();
    12             
    13             int x = 1/0;      //在此处模拟事务处理过程中出错
    14             
    15             String sql2 = "update account set money=money+100 where name='b'";
    16             st = conn.prepareStatement(sql2);
    17             st.executeUpdate();
    18             
    19             conn.commit();   //提交事务
    20         }catch (Exception e) {
    21             e.printStackTrace();
    22             conn.rollback();    //手动回滚该事务中所有的操作
    23         }finally{
    24             JdbcUtils.release(conn, st, rs);
    25         }
    26     }
    View Code

      效果和例2也是一样的,事务出错则回滚所有的操作。

    例4:指定事务回滚点的案例

      例3的手动回滚其实有些鸡肋,与其说是手动回滚,其实即使没有调用rollback该事务就不会执行成功,而rollback方法更高级的功能在于能回滚到指定的地方。

      通过链接Connection对象的setSavepoint()方法即可在该方法所在的位置设置回滚点对象,当调用rollback(回滚点对象)方法即可将事务回滚到这个位置。这样在执行回滚之后,再次调用提交事务(Commit),则回滚点之前的SQL还是执行的。

      以例2为前提,我们在会发生异常地方的前面设置回滚点,而使第一条SQL语句能被执行,即用户A的操作能执行(减少100),用户B的操作不能执行(金额不变)。

      所有表、表中数据、配置文件和JDBC工具类都同例2相同。

    将例2中的代码修改为如下:

     1 public void transaction() throws SQLException {
     2         Connection conn = null;
     3         PreparedStatement st = null;
     4         ResultSet rs = null;
     5         Savepoint sp = null;   //代表回滚点对象
     6         try{
     7             conn = JdbcUtils.getConnection();
     8             conn.setAutoCommit(false);  //开启事务,相当于start transaction命令
     9             
    10             String sql1 = "update account set money=money-100 where name='a'";
    11             st = conn.prepareStatement(sql1);
    12             st.executeUpdate();
    13             
    14             sp = conn.setSavepoint();   //在此处设置回滚点
    15             
    16             int x = 1/0;      //在此处模拟事务处理过程中出错
    17             
    18             String sql2 = "update account set money=money+100 where name='b'";
    19             st = conn.prepareStatement(sql2);
    20             st.executeUpdate();
    21             
    22             conn.commit();   //提交事务
    23         }catch (Exception e) {
    24             conn.rollback(sp);    //回滚到指定的回滚点处
    25             conn.commit();        //回滚之后再次提交事务保证回滚点之前的SQL能被执行
    26             
    27         }finally{
    28             JdbcUtils.release(conn, st, rs);
    29         }
    30     }
    View Code

    查看事务执行情况:

      

      可以看到结果正如我们希望的那样,由于用户A的操作在回滚点之前,又因为执行回滚之后还执行了提交事务,因此回滚点之前的SQL命令还是可以被执行成功的。因此切记,要想使用回滚点,一定要在回滚之后再次提交事务,否则设置回滚点是没有意义的。

      下图是一张上面例子的执行流程:

      

                  

  • 相关阅读:
    ASP.NET AJAX 's UpdatePanel 中使用Page.ClientScript.RegisterStartupScript 失败
    从struct到byte[]之RawFormatter
    一个HTTP.二进制POST和HTTP指定长度接收的C++实现
    发现移动太NB了,验证码图像路径直接包含验证码
    又一个网页下载者木马
    Trojan program TrojanDownloader.JS.IstBar.ai 病毒样本
    用控件的方式解决问题在客户端关联WEB控件引用
    需要防范的XSS攻击
    用"指定字符组成的任意进制"转换生成不带4的卡号
    人为漏洞的构造、文件的载入、验证机制的突破
  • 原文地址:https://www.cnblogs.com/fjdingsd/p/5272986.html
Copyright © 2020-2023  润新知