zoukankan      html  css  js  c++  java
  • 10.2(java学习笔记)JDBC事务简述

    一、事务

      事务是指作为一系列操作组成的一个整体,该整体只有两种状态,要么全部执行,要么全部不执行。

      当组成这个事务的所有语句都执行成功则该事务执行,只要有一条语句执行失败则该事务不执行。

      假设这里有一个insert语句和一个update语句属于一个事务,从宏观上来看,这个事务的状态只有执行或者不执行。

      从微观上看,这个事务是由这两条语句组成的, 每个语句必定有其个体的状态(成功或者不成功)。

      比如可能是insert成功,update失败。也可能是insert失败,update成功。

      当其中只要有一条语句失败,则发送回滚(回到该事务执行前的状态即所有事务都不执行,已成功执行的语句会作废)。

      该事务不执行。如果所有语句都成功,则该事务执行。

      那么事务是如何划分的呢,怎么样算事务的开始,怎么样又算事务的结束呢?

      2.1事务开始

        -连接到数据库上,并执行了一条DML语句(insert、update、delete)。

        -前一个事务结束后,新输入的DML语句。

      2.2事务结束

        -执行commit,或rollback语句。

        -执行一条DDL语句,如create table,这种情况下回自动执行commit语句。

        -执行一条DCL语句,如grant语句,在这种情况下回自动执行commit语句。

        -断开与数据库连接

        -执行一条DML语句出现错误时,在这种情况下为这个无效的语句执行rollback。

        DDL、DCL、DML可参阅:https://www.cnblogs.com/kawashibara/p/8961646.html

        假设这里有五条语句:

          -select

          -insert   ---事务开始---

          -updata

          -delete

          -insert    ---

          commit or DCL or DDL  ---

         结合上面分析,事务从insert开始,如果所有语句都能正常执行,

        那么事务从insert开始,到commit or DCL or DDL结束,这代表一个事务。

        假如其中只要有一条DML语句执行错误,比如第二个insert执行错误,

        那么事务从第一个insert开始,第二个到insert结束。这个事务呈现出一种不执行的状态。

        即insert updata delete insert都不执行,可以看做回到了第一个insert之前的状态(回滚rollback)。

        这里需要用到一个函数:

        void rollback();撤消当前事务中所做的所有更改,并释放此连接对象当前支持的任何数据库锁。

        仅当禁用自动提交模式时才应使用此方法。

        但DML语句出现异常时,我们需要调用此方法达到回滚(回到该事务执行前的状态,即该事务没有执行)。

        

        我们先来看这样一个例子:

        insert(正常执行) --> insert(正常执行) -->commit();

        事务从insert开始,到commit结束。这整个事务是执行的。(测试前最好把表清空下,避免插入重复的主键导致错误)

    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    public class TestJDBC{
        public static void main(String[] args){
            final String connectionUrl = "jdbc:mysql://localhost:3306/mybatis";
            String userName = "root";
            String passWord = "123456";
            String insertT = "INSERT INTO `mybatis`.`tadd`"  //正确的insert语句
                    + "(`id`, `tname`, `tpwd`, `tstudentnum`) "
                    + "VALUES (?, ?, ?, ?);";
            String insertF = "INSERT INTO `mybatis`.`tadd`"  //错误的insert语句
                    + "(`id`, `tname`, `tpwd`, `tstudentnum`) "
                    + "VALUES (?, ?, ?, ?, ?, ?);";
            Connection conn = null;
            PreparedStatement p1 = null;
            PreparedStatement p2 = null;
    
            try {
                //加载驱动
                Class.forName("com.mysql.cj.jdbc.Driver");
                //建立连接
                conn = DriverManager.getConnection(connectionUrl,userName,passWord);
                conn.setAutoCommit(false);//将自动提交设为false,即不进行自动提交
                
                p1 = conn.prepareStatement(insertT);//第一个insert语句,事务开始(正确的语句)
                setParam(p1,"1","1","1","1");
                p1.execute();
                System.out.println("p1插入完成");
                
                Thread.sleep(5000);//休眠5s
                
                p2 = conn.prepareStatement(insertT);//第二个insert语句  (正确的语句)
                setParam(p2,"2","2","2","2");
                p2.execute();
                System.out.println("p2插入完成");
                conn.commit();//手动提交
                
                System.out.println("执行成功");    
            } catch (SQLException e) {
                try {
                    conn.rollback();//事务回滚
                } catch (SQLException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }finally{
                //依次关闭连接
                try{
                    p1.close();
                }catch(Exception e){
                    
                }
                try{
                    p2.close();
                }catch(Exception e){
                
                }
                try{
                    conn.close();
                }catch(Exception e){
                }
            }
        }
        public static void setParam(PreparedStatement p,Object... o){
            int i = 1;
            for(Object temp:o){
                try {
                    p.setObject(i, temp);
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                i++;
            }
        }
    }
    运行结果:
    p1插入完成
    p2插入完成
    执行成功

    p1插入成功后会等待5s,5s之后p2进行插入,到最后commit整个事务的状态是执行的。

    我们可以看下数据库中的数据是插入成功的。

        接下来我们去看这样一个例子:

        insert(正常执行) --> insert(执行错误) -->commit();

        事务从insert开始,到错误的insert结束,整个事务是不执行的。

        即第一个insert不执行,第二个insert也不执行。

       (由于测试数据依然采用之前的数据,所以测试前需先将表清空)  

    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    public class TestJDBC{
        public static void main(String[] args){
            final String connectionUrl = "jdbc:mysql://localhost:3306/mybatis";
            String userName = "root";
            String passWord = "123456";
            String insertT = "INSERT INTO `mybatis`.`tadd`"
                    + "(`id`, `tname`, `tpwd`, `tstudentnum`) "
                    + "VALUES (?, ?, ?, ?);";
            String insertF = "INSERT INTO `mybatis`.`tadd`"
                    + "(`id`, `tname`, `tpwd`, `tstudentnum`) "
                    + "VALUES (?, ?, ?, ?, ?, ?);";
            Connection conn = null;
            PreparedStatement p1 = null;
            PreparedStatement p2 = null;
    
            try {
                //加载驱动
                Class.forName("com.mysql.cj.jdbc.Driver");
                //建立连接
                conn = DriverManager.getConnection(connectionUrl,userName,passWord);
                conn.setAutoCommit(false);//将自动提交设为false,即不进行自动提交
                
                p1 = conn.prepareStatement(insertT);//正确的insert语句
                setParam(p1,"1","1","1","1");
                p1.execute();
                System.out.println("p1插入完成");
                
                Thread.sleep(5000);
                
                p2 = conn.prepareStatement(insertF);//错误insert的语句
                setParam(p2,"2","2","2","2");
                p2.execute();
                System.out.println("p2插入完成");
                conn.commit();//手动提交
                
                System.out.println("执行成功");    
            } catch (SQLException e) {
                try {
                    conn.rollback();//事务回滚
                } catch (SQLException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }finally{
                //依次关闭连接
                try{
                    p1.close();
                }catch(Exception e){
                    
                }
                try{
                    p2.close();
                }catch(Exception e){
                
                }
                try{
                    conn.close();
                }catch(Exception e){
                }
            }
        }
        public static void setParam(PreparedStatement p,Object... o){
            int i = 1;
            for(Object temp:o){
                try {
                    p.setObject(i, temp);
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                i++;
            }
        }
    }
    运行结果;
    p1插入完成
    java.sql.SQLException: No value specified for parameter 5
        at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:127)
        at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:95)
        at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
        at com.mysql.cj.jdbc.ClientPreparedStatement.execute(ClientPreparedStatement.java:414)
        at com.test.jdbc.TestJDBC.main(TestJDBC.java:41)

    我们看下数据库中的数据:

    我们会发现,即使第一条插入语句执行了,但由于这个事务中第二条insert发生了错误,所以这个事务是不执行的。

  • 相关阅读:
    第二章例2-9
    第二章例2-8
    第二章例2-7
    第二章例2-6
    第二章例2-5
    第二章例2-4
    第二章例2-3
    第二章例2-2
    第二章例2-1
    第一章例1-2
  • 原文地址:https://www.cnblogs.com/huang-changfan/p/10251502.html
Copyright © 2011-2022 走看看