zoukankan      html  css  js  c++  java
  • Java回顾之JDBC

      第一篇:Java回顾之I/O

      第二篇:Java回顾之网络通信

      第三篇:Java回顾之多线程

      第四篇:Java回顾之多线程同步

      第五篇:Java回顾之集合

      第六篇:Java回顾之序列化

      第七篇:Java回顾之反射

      第八篇:Java回顾之一些基础概念

      这篇文章里,我们来讨论一些和JDBC相关的话题。

      概述

      尽管在实际开发过程中,我们一般使用ORM框架来代替传统的JDBC,例如Hibernate或者iBatis,但JDBC是Java用来实现数据访问的基础,掌握它对于我们理解Java的数据操作流程很有帮助。

      JDBC的全称是Java Database Connectivity。

      JDBC对数据库进行操作的流程:

    • 连接数据库
    • 发送数据请求,即传统的CRUD指令
    • 返回操作结果集

      JDBC中常用的对象包括:

    • ConnectionManager
    • Connection
    • Statement
    • CallableStatement
    • PreparedStatement
    • ResultSet
    • SavePoint

      一个简单示例

      我们来看下面一个简单的示例,它使用JDK自带的Derby数据库,创建一张表,插入一些记录,然后将记录返回:

    一个简单的JDBC示例
     1 private static void test1() throws SQLException
     2 {
     3     String driver = "org.apache.derby.jdbc.EmbeddedDriver";
     4     String dbURL = "jdbc:derby:EmbeddedDB;create=true";
     5     
     6     Connection con = null;
     7     Statement st = null;
     8     try
     9     {
    10         Class.forName(driver);
    11         con = DriverManager.getConnection(dbURL);
    12         st = con.createStatement();
    13         st.execute("create table foo(ID INT NOT NULL, NAME VARCHAR(30))");
    14         st.executeUpdate("insert into foo(ID,NAME) values(1, 'Zhang San')");
    15         
    16         ResultSet rs = st.executeQuery("select ID,NAME from foo");
    17         
    18         while(rs.next())
    19         {
    20             int id = rs.getInt("ID");
    21             String name = rs.getString("NAME");
    22             System.out.println("ID=" + id + "; NAME=" + name);
    23         }
    24     }
    25     catch(Exception ex)
    26     {
    27         ex.printStackTrace();
    28     }
    29     finally
    30     {
    31         if (st != null) st.close();
    32         if (con != null) con.close();
    33     }
    34 }

      如何建立数据库连接

      上面的示例代码中,建立数据库连接的部分如下:

    String driver = "org.apache.derby.jdbc.EmbeddedDriver";
    String dbURL = "jdbc:derby:EmbeddedDB;create=true";
    
    Class.forName(driver);
    con = DriverManager.getConnection(dbURL);

      建立数据库连接的过程,可以分为两步:

      1)加载数据库驱动,即上文中的driver以及Class.forName(dirver)

      2)定位数据库连接字符串, 即dbURL以及DriverManager.getConnection(dbURL)

      不同的数据库,对应的dirver和dbURL不同,但加载驱动和建立连接的方式是相同的,即只需要修改上面driver和dbURL的值就可以了。

      自动加载数据库驱动

      如果我们每次建立连接时,都要使用Class.forName(...)来手动加载数据库驱动,这样会很麻烦,我们可以通过配置文件的方式,来保存数据库驱动的信息。

      我们可以在classpath中,即编译出来的.class的存放路径,添加如下文件:

    META-INF\services\java.sql.Driver

      对应的内容就是JDBC驱动的全路径,也就是上面driver变量的值:

    org.apache.derby.jdbc.EmbeddedDriver

      接下来,我们在程序中,就不需要再显示的用Class.forName(...)来加载驱动了,它会被自动加载进来,当我们的数据库发生变化时,只需要修改这个文件就可以了,例如当我们的数据库由Derby变为MySQL时,只需要将上述的配置修改为:

    com.mysql.jdbc.Driver

      但是,需要注意一点,这里只是配置了JDBC驱动的全路径,并没有包含jar文件的信息,因此,我们还是需要将包含该驱动的jar文件手动的放置到程序的classpath中。

      JDBC中的基本操作

      对于数据库操作来说,CRUD操作应该是最常见的操作了, 即我们常说的增、删、查、改。

      JDBC是使用Statement和ResultSet来完成这些操作的。

      如何实现CRUD

      下面是一个实现CRUD的示例:

    JDBC实现基本的CRUD示例
     1 private static void insertTest() throws SQLException
     2 {
     3     String dbURL = "jdbc:mysql://localhost/test";
     4     Connection con = DriverManager.getConnection(dbURL, "root", "123");
     5     Statement st = con.createStatement();
     6     st.execute("insert into user(ID,NAME) values(1, 'Zhang San')");
     7     st.execute("insert into user(ID,NAME) values(2, 'Li Si')");
     8     st.execute("insert into user(ID,NAME) values(3, 'Wang Wu')");
     9     System.out.println("=====insert test=====");
    10     showUser(st);
    11     st.close();
    12     con.close();
    13 }
    14 
    15 private static void deleteTest() throws SQLException
    16 {
    17     String dbURL = "jdbc:mysql://localhost/test";
    18     Connection con = DriverManager.getConnection(dbURL, "root", "123");
    19     Statement st = con.createStatement();
    20     st.execute("delete from user where ID=3");
    21     System.out.println("=====delete test=====");
    22     showUser(st);
    23     st.close();
    24     con.close();
    25 }
    26 
    27 private static void updateTest() throws SQLException
    28 {
    29     String dbURL = "jdbc:mysql://localhost/test";
    30     Connection con = DriverManager.getConnection(dbURL, "root", "123");
    31     Statement st = con.createStatement();
    32     st.executeUpdate("update user set NAME='TEST' where ID=2");
    33     System.out.println("=====update test=====");
    34     showUser(st);
    35     st.close();
    36     con.close();
    37 }
    38 
    39 private static void showUser(Statement st) throws SQLException
    40 {
    41     ResultSet rs = st.executeQuery("select ID, NAME from user");
    42     while(rs.next())
    43     {
    44         int id = rs.getInt("ID");
    45         String name = rs.getString("NAME");
    46         System.out.println("ID:" + id + "; NAME=" + name);
    47     }
    48     rs.close();
    49 }

      我们顺序调用上面的测试方法:

    1 insertTest();
    2 deleteTest();
    3 updateTest();

      执行结果如下:

    =====insert test=====
    ID:1; NAME=Zhang San
    ID:2; NAME=Li Si
    ID:3; NAME=Wang Wu
    =====delete test=====
    ID:1; NAME=Zhang San
    ID:2; NAME=Li Si
    =====update test=====
    ID:1; NAME=Zhang San
    ID:2; NAME=TEST

      上面代码中的showUser方法会把user表中的所有记录打印出来。

      如何调用存储过程

      存储过程是做数据库开发时经常使用的技术,它可以通过节省编译时间的方式来提升系统性能,我们这里的示例使用MySQL数据库。

      如何调用不带参数的存储过程

      假设我们现在有一个简单的存储过程,它只是返回user表中的所有记录,存储过程如下:

    1 CREATE DEFINER=`root`@`localhost` PROCEDURE `GetUser`()
    2 BEGIN
    3 select ID,NAME from user;
    4 END

      我们可以使用CallableStatement来调用存储过程:

    调用存储过程示例一
     1 private static void execStoredProcedureTest() throws SQLException
     2 {
     3     String dbURL = "jdbc:mysql://localhost/test";
     4     Connection con = DriverManager.getConnection(dbURL, "root", "123");
     5     CallableStatement cst = con.prepareCall("call GetUser()");
     6     ResultSet rs = cst.executeQuery();
     7     while(rs.next())
     8     {
     9         int id = rs.getInt("ID");
    10         String name = rs.getString("NAME");
    11         System.out.println("ID:" + id + "; NAME=" + name);
    12     }
    13     rs.close();
    14     cst.close();
    15     con.close();
    16 }

      它的执行结果如下:

    ID:1; NAME=Zhang San
    ID:2; NAME=TEST

      如何调用带参数的存储过程

      MySQL的存储过程中的参数分为三种:in/out/inout,我们可以把in看做入力参数,out看做出力参数,JDBC对这两种类型的参数设置方式不同:

      1)in, JDBC使用类似于cst.set(1, 10)的方式来设置

      2)out,JDBC使用类似于cst.registerOutParameter(2, Types.VARCHAR);的方式来设置

      我们来看一个in参数的示例,假设我们希望返回ID为特定值的user信息,存储过程如下:

    1 CREATE DEFINER=`root`@`localhost` PROCEDURE `GetUserByID`(in id int)
    2 BEGIN
    3 set @sqlstr=concat('select * from user where ID=', id);
    4 prepare psmt from @sqlstr;
    5 execute psmt;
    6 END

      Java的调用代码如下:

    JDBC调用存储过程示例二
     1 private static void execStoredProcedureTest2(int id) throws SQLException
     2 {
     3     String dbURL = "jdbc:mysql://localhost/test";
     4     Connection con = DriverManager.getConnection(dbURL, "root", "123");
     5     CallableStatement cst = con.prepareCall("call GetUserByID(?)");
     6     cst.setInt(1, id);
     7     ResultSet rs = cst.executeQuery();
     8     while(rs.next())
     9     {
    10         String name = rs.getString("NAME");
    11         System.out.println("ID:" + id + "; NAME=" + name);
    12     }
    13     rs.close();
    14     cst.close();
    15     con.close();
    16 }

      我们执行下面的语句:

    execStoredProcedureTest2(1);

      结果如下:

    ID:1; NAME=Zhang San

      对于out类型的参数,调用方式类似,不再赘述。

      获取数据库以及结果集的metadata信息

      在JDBC中,我们不仅能够对数据进行操作,我们还能获取数据库以及结果集的元数据信息,例如数据库的名称、驱动信息、表信息;结果集的列信息等。

      获取数据库的metadata信息

      我们可以通过connection.getMetaData方法来获取数据库的元数据信息,它的类型是DatabaseMetaData。

    获取数据库的元数据信息
     1 private static void test1() throws SQLException
     2 {
     3     String dbURL = "jdbc:mysql://localhost/mysql";
     4     Connection con = DriverManager.getConnection(dbURL, "root", "123");
     5     
     6     DatabaseMetaData dbmd = con.getMetaData();
     7     
     8     System.out.println("数据库:" + dbmd.getDatabaseProductName() + " " + dbmd.getDatabaseProductVersion());
     9     System.out.println("驱动程序:" + dbmd.getDriverName() + " " + dbmd.getDriverVersion());
    10 
    11     ResultSet rs = dbmd.getTables(null, null, null, null);
    12     System.out.println(String.format("|%-26s|%-9s|%-9s|%-9s|", "表名称","表类别","表类型","表模式"));        
    13     while(rs.next())
    14     {
    15         System.out.println(String.format("|%-25s|%-10s|%-10s|%-10s|", 
    16                 rs.getString("TABLE_NAME"),rs.getString("TABLE_CAT"),
    17                 rs.getString("TABLE_TYPE"), rs.getString("TABLE_SCHEM")));
    18     }
    19 }

      这里我们使用的数据库是MySQL中自带的默认数据库:mysql,它会记录整个数据库服务器中的一些信息。上述代码执行结果如下:

    数据库:MySQL 5.5.28
    驱动程序:MySQL-AB JDBC Driver mysql-connector-java-5.0.4 ( $Date: 2006-10-19 17:47:48 +0200 (Thu, 19 Oct 2006) $, $Revision: 5908 $ )
    |表名称                       |表类别      |表类型      |表模式      |
    |columns_priv             |mysql     |TABLE     |null      |
    |db                       |mysql     |TABLE     |null      |
    |event                    |mysql     |TABLE     |null      |
    |func                     |mysql     |TABLE     |null      |
    。。。

      由于mysql中表比较多,上述结果只截取了一部分。

      获取结果集的元数据信息

      我们可以通过使用resultset.getMetaData方法来获取结果集的元数据信息,它的类型是ResultSetMetaData。

    获取结果集的元数据信息
     1 private static void test2() throws SQLException
     2 {
     3     String dbURL = "jdbc:mysql://localhost/test";
     4     Connection con = DriverManager.getConnection(dbURL, "root", "123");
     5     Statement st = con.createStatement();
     6     ResultSet rs = st.executeQuery("select ID, NAME from user");
     7     ResultSetMetaData rsmd = rs.getMetaData();
     8     for (int i = 1; i <= rsmd.getColumnCount(); i++)
     9     {
    10         System.out.println("Column Name:" + rsmd.getColumnName(i) + "; Column Type:" + rsmd.getColumnTypeName(i));
    11     }
    12 }

      它的执行结果如下:

    Column Name:ID; Column Type:INTEGER UNSIGNED
    Column Name:NAME; Column Type:VARCHAR

      可以看到,它返回类结果集中每一列的名称和类型。

      基于ResultSet的操作

      当我们需要对数据库进行修改时,除了上述通过Statement完成操作外,我们也可以借助ResultSet来完成。

      需要注意的是,在这种情况下,我们定义Statement时,需要添加参数。

      Statement构造函数可以包含3个参数:

    • resultSetType,它的取值包括:ResultSet.TYPE_FORWARD_ONLYResultSet.TYPE_SCROLL_INSENSITIVEResultSet.TYPE_SCROLL_SENSITIVE,默认情况下,该参数的值是ResultSet.TYPE_FORWARD_ONLY
    • resultSetConcurrency,它的取值包括:ResultSet.CONCUR_READ_ONLYResultSet.CONCUR_UPDATABLE,默认情况下,该参数的值是ResultSet.CONCUR_READ_ONLY
    • resultSetHoldability,它的取值包括:ResultSet.HOLD_CURSORS_OVER_COMMITResultSet.CLOSE_CURSORS_AT_COMMIT

      为了使得ResultSet能够对数据进行操作我们需要:

    • 将resultSetType设置为ResultSet.TYPE_SCROLL_SENSITIVE
    • 将resultSetConcurrency设置为ResultSet.CONCUR_UPDATABLE

      在通过ResultSet对数据进行调整的过程中,下面方法可能会被调用:

    • resultset.last()
    • resultset.first()
    • resultset.moveToInsertRow()
    • resultset.absolute()
    • resultset.setxxx()
    • resultset.updateRow()
    • resultset.insertRow()

      下面是一个通过ResultSet对数据进行增、删、改的示例:

    通过ResultSet对数据进行增、删、改
     1 private static void getResultCount() throws SQLException
     2 {
     3     System.out.println("=====Result Count=====");
     4     String dbURL = "jdbc:mysql://localhost/test";
     5     Connection con = DriverManager.getConnection(dbURL, "root", "123");
     6     Statement st = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY, ResultSet.CLOSE_CURSORS_AT_COMMIT);
     7     ResultSet rs = st.executeQuery("select * from user");
     8     rs.last();
     9     System.out.println("返回结果的条数:"+ rs.getRow());
    10     rs.first();
    11     
    12     rs.close();
    13     st.close();
    14     con.close();
    15 }
    16 
    17 private static void insertDataToResultSet() throws SQLException
    18 {
    19     System.out.println("=====Insert=====");
    20     String dbURL = "jdbc:mysql://localhost/test";
    21     Connection con = DriverManager.getConnection(dbURL, "root", "123");
    22     Statement st = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
    23     ResultSet rs = st.executeQuery("select ID,NAME from user");
    24     rs.moveToInsertRow();
    25     rs.updateInt(1, 4);
    26     rs.updateString(2, "Xiao Ming");
    27     rs.insertRow();
    28     showUser(st);
    29     
    30     rs.close();
    31     st.close();
    32     con.close();
    33 }
    34 
    35 private static void updateDataToResultSet() throws SQLException
    36 {
    37     System.out.println("=====Update=====");
    38     String dbURL = "jdbc:mysql://localhost/test";
    39     Connection con = DriverManager.getConnection(dbURL, "root", "123");
    40     Statement st = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
    41     ResultSet rs = st.executeQuery("select * from user");
    42     rs.last();
    43     int count = rs.getRow();
    44     rs.first();
    45     rs.absolute(count);
    46     rs.updateString(2, "Xiao Qiang");
    47     rs.updateRow();
    48     showUser(st);
    49     
    50     rs.close();
    51     st.close();
    52     con.close();
    53 }
    54 
    55 private static void delDataFromResultSet() throws SQLException
    56 {
    57     System.out.println("=====Delete=====");
    58     String dbURL = "jdbc:mysql://localhost/test";
    59     Connection con = DriverManager.getConnection(dbURL, "root", "123");
    60     Statement st = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE, ResultSet.CLOSE_CURSORS_AT_COMMIT);
    61     ResultSet rs = st.executeQuery("select * from user");
    62     rs.last();
    63     int count = rs.getRow();
    64     rs.first();
    65     rs.absolute(count);
    66     rs.deleteRow();
    67     showUser(st);
    68     
    69     rs.close();
    70     st.close();
    71     con.close();
    72 }

      分别调用上述方法:

    1 getResultCount();
    2 insertDataToResultSet();
    3 updateDataToResultSet();
    4 delDataFromResultSet();

      执行结果如下:

    =====Result Count=====
    返回结果的条数:2
    =====Insert=====
    ID:1; NAME=Zhang San
    ID:2; NAME=TEST
    ID:4; NAME=Xiao Ming
    =====Update=====
    ID:1; NAME=Zhang San
    ID:2; NAME=TEST
    ID:4; NAME=Xiao Qiang
    =====Delete=====
    ID:1; NAME=Zhang San
    ID:2; NAME=TEST

      可以看到我们对ID为4的记录进行了插入、更新和删除操作。

      预处理以及批处理

      预处理和批处理都是用来提升系统性能的方式,一种是利用数据库的缓存机制,一种是利用数据库一次执行多条语句的方式。

      预处理

      数据库服务器接收到Statement后,一般会解析Statement、分析是否有语法错误、定制最优的执行计划,这个过程可能会降低系统的性能。一般的数据库服务器都这对这种情况,设计了缓存机制,当数据库接收到指令时,如果缓存中已经存在,那么就不再解析,而是直接运行。

      这里相同的指令是指sql语句完全一样,包括大小写。

      JDBC使用PreparedStatement来完成预处理:

    预处理示例
     1 private static void test1() throws SQLException
     2 {
     3     System.out.println("=====Insert a single record by PreparedStatement=====");
     4     String dbURL = "jdbc:mysql://localhost/test";
     5     Connection con = DriverManager.getConnection(dbURL, "root", "123");
     6     PreparedStatement pst = con.prepareStatement("insert into user(id,name) values(?,?)");
     7     pst.setInt(1, 5);
     8     pst.setString(2, "Lei Feng");
     9     pst.executeUpdate();
    10     showUser(pst);
    11     pst.close();
    12     con.close();
    13 }

      执行结果如下:

    =====Insert a single record by PreparedStatement=====
    ID:1; NAME=Zhang San
    ID:2; NAME=TEST
    ID:5; NAME=Lei Feng

      批处理

      批处理是利用数据库一次执行多条语句的机制来提升性能,这样可以避免多次建立连接带来的性能损失。

      批处理使用Statement的addBatch来添加指令,使用executeBatch方法来一次执行多条指令:

    批处理示例
     1 private static void test2() throws SQLException
     2 {
     3     System.out.println("=====Insert multiple records by Statement & Batch=====");
     4     String dbURL = "jdbc:mysql://localhost/test";
     5     Connection con = DriverManager.getConnection(dbURL, "root", "123");
     6     Statement st = con.createStatement();
     7     st.addBatch("insert into user(id,name) values(6,'Xiao Zhang')");
     8     st.addBatch("insert into user(id,name) values(7,'Xiao Liu')");
     9     st.addBatch("insert into user(id,name) values(8,'Xiao Zhao')");
    10     st.executeBatch();
    11     showUser(st);
    12     st.close();
    13     con.close();
    14 }

      执行结果如下:

    =====Insert multiple records by Statement & Batch=====
    ID:1; NAME=Zhang San
    ID:2; NAME=TEST
    ID:5; NAME=Lei Feng
    ID:6; NAME=Xiao Zhang
    ID:7; NAME=Xiao Liu
    ID:8; NAME=Xiao Zhao

      预处理和批处理相结合

      我们可以把预处理和批处理结合起来,利用数据库的缓存机制,一次执行多条语句:

    预处理和批处理相结合的示例
     1 private static void test3() throws SQLException
     2 {
     3     System.out.println("=====Insert multiple records by PreparedStatement & Batch=====");
     4     String dbURL = "jdbc:mysql://localhost/test";
     5     Connection con = DriverManager.getConnection(dbURL, "root", "123");
     6     PreparedStatement pst = con.prepareStatement("insert into user(id,name) values(?,?)");
     7     pst.setInt(1, 9);
     8     pst.setString(2, "Xiao Zhang");
     9     pst.addBatch();
    10     pst.setInt(1, 10);
    11     pst.setString(2, "Xiao Liu");
    12     pst.addBatch();
    13     pst.setInt(1, 11);
    14     pst.setString(2, "Xiao Zhao");
    15     pst.addBatch();
    16     pst.executeBatch();
    17     showUser(pst);
    18     pst.close();
    19     con.close();
    20 }

      执行结果如下:

    =====Insert multiple records by PreparedStatement & Batch=====
    ID:1; NAME=Zhang San
    ID:2; NAME=TEST
    ID:5; NAME=Lei Feng
    ID:9; NAME=Xiao Zhang
    ID:10; NAME=Xiao Liu
    ID:11; NAME=Xiao Zhao

      数据库事务

      谈到数据库开发,事务是一个不可回避的话题,JDBC默认情况下,是每一步都自动提交的,我们可以通过设置connection.setAutoCommit(false)的方式来强制关闭自动提交,然后通过connection.commit()和connection.rollback()来实现事务提交和回滚。

      简单的数据库事务

      下面是一个简单的数据库事务的示例:

    简单的数据库事务示例
     1 private static void transactionTest1() throws SQLException
     2 {
     3     System.out.println("=====Simple Transaction test=====");
     4     String dbURL = "jdbc:mysql://localhost/test";
     5     Connection con = DriverManager.getConnection(dbURL, "root", "123");
     6     Statement st = con.createStatement();
     7     try
     8     {
     9         con.setAutoCommit(false);
    10         st.executeUpdate("insert into user(id,name) values(12, 'Xiao Li')");
    11         con.commit();
    12     }
    13     catch(Exception ex)
    14     {
    15         ex.printStackTrace();
    16         con.rollback();
    17     }
    18     finally
    19     {
    20         con.setAutoCommit(true);
    21         showUser(st);
    22         if (st != null) st.close();
    23         if (con != null) con.close();
    24     }
    25 }

      连续执行上述方法两次,我们可以得出下面的结果:

    =====Simple Transaction test=====
    ID:1; NAME=Zhang San
    ID:2; NAME=TEST
    ID:5; NAME=Lei Feng
    ID:12; NAME=Xiao Li
    =====Simple Transaction test=====
    ID:1; NAME=Zhang San
    ID:2; NAME=TEST
    ID:5; NAME=Lei Feng
    ID:12; NAME=Xiao Li
    com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException: Duplicate entry '12' for key 'PRIMARY'
        at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:931)
        at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:2870)
        at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1573)
        at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:1665)
        at com.mysql.jdbc.Connection.execSQL(Connection.java:3170)
        at com.mysql.jdbc.Statement.executeUpdate(Statement.java:1316)
        at com.mysql.jdbc.Statement.executeUpdate(Statement.java:1235)
        at sample.jdbc.mysql.ResultSetSample.transactionTest1(ResultSetSample.java:154)
        at sample.jdbc.mysql.ResultSetSample.main(ResultSetSample.java:17)

      可以看到,第一次调用时,操作成功,事务提交,向user表中插入了一条记录;第二次调用时,发生主键冲突异常,事务回滚。

      带有SavePoint的事务

      当我们的事务操作中包含多个处理,但我们有时希望一些操作完成后可以先提交,这样可以避免整个事务的回滚。JDBC使用SavePoint来实现这一点。

    带有SavePoint的事务示例
     1 private static void transactionTest2() throws SQLException
     2 {
     3     System.out.println("=====Simple Transaction test=====");
     4     String dbURL = "jdbc:mysql://localhost/test";
     5     Connection con = DriverManager.getConnection(dbURL, "root", "123");
     6     Statement st = con.createStatement();
     7     Savepoint svpt = null;
     8     try
     9     {
    10         con.setAutoCommit(false);
    11         st.executeUpdate("insert into user(id,name) values(13, 'Xiao Li')");
    12         st.executeUpdate("insert into user(id,name) values(14, 'Xiao Wang')");
    13         svpt = con.setSavepoint("roll back to here");
    14         st.executeUpdate("insert into user(id,name) values(15, 'Xiao Zhao')");
    15         st.executeUpdate("insert into user(id,name) values(13, 'Xiao Li')");
    16         con.commit();
    17     }
    18     catch(Exception ex)
    19     {
    20         ex.printStackTrace();
    21         con.rollback(svpt);
    22     }
    23     finally
    24     {
    25         con.setAutoCommit(true);
    26         showUser(st);
    27         if (st != null) st.close();
    28         if (con != null) con.close();
    29     }
    30 }

      执行结果如下:

    =====Simple Transaction test=====
    com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException: Duplicate entry '13' for key 'PRIMARY'
        at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:931)
        at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:2870)
        at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1573)
        at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:1665)
        at com.mysql.jdbc.Connection.execSQL(Connection.java:3170)
        at com.mysql.jdbc.Statement.executeUpdate(Statement.java:1316)
        at com.mysql.jdbc.Statement.executeUpdate(Statement.java:1235)
        at sample.jdbc.mysql.ResultSetSample.transactionTest2(ResultSetSample.java:185)
        at sample.jdbc.mysql.ResultSetSample.main(ResultSetSample.java:18)
    ID:1; NAME=Zhang San
    ID:2; NAME=TEST
    ID:5; NAME=Lei Feng
    ID:13; NAME=Xiao Li
    ID:14; NAME=Xiao Wang

      可以看到最终事务报出了主键冲突异常,事务回滚,但是依然向数据库中插入了ID为13和14的记录。

      另外,在确定SavePoint后,ID为15的记录并没有被插入,它是通过事务进行了回滚。

  • 相关阅读:
    PHP保留小数的相关方法
    ASP.NET Core MVC 之过滤器(Filter)
    ASP.NET Core MVC 之控制器(Controller)
    ASP.NET Core MVC 之视图组件(View Component)
    ASP.NET Core MVC 之局部视图(Partial Views)
    标签助手(TagHelper)
    ASP.NET Core MVC 之布局(Layout)
    ASP.NET Core MVC 之视图(Views)
    ASP.NET Core MVC 之模型(Model)
    九卷读书:淘宝从小到大的发展 -重读《淘宝技术这十年》
  • 原文地址:https://www.cnblogs.com/wing011203/p/3073838.html
Copyright © 2011-2022 走看看