} } }

    Java回顾之JDBC

    添加时间:2013-5-14 点击量:

      第一篇: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( into foo(ID,NAME) values(1, Zhang San));
    15
    16 ResultSet rs = st.executeQuery( ID,NAME 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 Test() 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( into user(ID,NAME) values(1, Zhang San));
    7 st.execute( into user(ID,NAME) values(2, Li Si));
    8 st.execute( into user(ID,NAME) values(3, Wang Wu));
    9 System.out.println(===== test=====);
    10 showUser(st);
    11 st.close();
    12 con.close();
    13 }
    14
    15 private static void Test() 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( user where ID=3);
    21 System.out.println(===== test=====);
    22 showUser(st);
    23 st.close();
    24 con.close();
    25 }
    26
    27 private static void Test() 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( user set NAME=TEST where ID=2);
    33 System.out.println(===== 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( ID, NAME 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 Test();
    
    2 Test();
    3 Test();


      履行成果如下:



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


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


      如何调用存储过程


      存储过程是做数据库开辟时经常应用的技巧,它可以经由过程节俭编译时候的体式格式来提拔体系机能,我们这里的示例应用MySQL数据库。


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


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



    1 CREATE DEFINER=`root`@`localhost` PROCEDURE `GetUser`()
    
    2 BEGIN
    3 ID,NAME 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( user where ID=, id);
    4 prepare psmt @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(nullnullnullnull);
    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( ID, NAME user);
    7 ResultSetMetaData rsmd = rs.getMetaData();
    8 forint 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.Row()

    • resultset.Row()


      下面是一个经由过程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( 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 DataToResultSet() 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( ID,NAME user);
    24 rs.moveToInsertRow();
    25 rs.Int(1, 4);
    26 rs.String(2, Xiao Ming);
    27 rs.Row();
    28 showUser(st);
    29
    30 rs.close();
    31 st.close();
    32 con.close();
    33 }
    34
    35 private static void DataToResultSet() 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( user);
    42 rs.last();
    43 int count = rs.getRow();
    44 rs.first();
    45 rs.absolute(count);
    46 rs.String(2, Xiao Qiang);
    47 rs.Row();
    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( user);
    62 rs.last();
    63 int count = rs.getRow();
    64 rs.first();
    65 rs.absolute(count);
    66 rs.Row();
    67 showUser(st);
    68
    69 rs.close();
    70 st.close();
    71 con.close();
    72 }



      分别调用上述办法:



    1 getResultCount();
    
    2 DataToResultSet();
    3 DataToResultSet();
    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( 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( into user(id,name) values(6,Xiao Zhang));
    8 st.addBatch( into user(id,name) values(7,Xiao Liu));
    9 st.addBatch( 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( 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( 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( into user(id,name) values(13, Xiao Li));
    12 st.executeUpdate( into user(id,name) values(14, Xiao Wang));
    13 svpt = con.setSavepoint(roll back to here);
    14 st.executeUpdate( into user(id,name) values(15, Xiao Zhao));
    15 st.executeUpdate( 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的记录并没有入,它是经由过程事务进行了回滚。

    我俩之间有着强烈的吸引力。短短几个小时后,我俩已经明白:我们的心是一个整体的两半,我俩的心灵是孪生兄妹,是知己。她让我感到更有活力,更完美,更幸福。即使她不在我身边,我依然还是感到幸福,因为她总是以这样或者那样的方式出现在我心头。——恩里克·巴里奥斯《爱的文明》
    分享到: