欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

基于Java回顧之JDBC的使用詳解

 更新時(shí)間:2013年05月15日 16:29:09   作者:  
本篇文章是對(duì)Java中JDBC的使用進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下

盡管在實(shí)際開(kāi)發(fā)過(guò)程中,我們一般使用ORM框架來(lái)代替?zhèn)鹘y(tǒng)的JDBC,例如Hibernate或者iBatis,但JDBC是Java用來(lái)實(shí)現(xiàn)數(shù)據(jù)訪問(wèn)的基礎(chǔ),掌握它對(duì)于我們理解Java的數(shù)據(jù)操作流程很有幫助。

JDBC的全稱(chēng)是Java Database Connectivity。

JDBC對(duì)數(shù)據(jù)庫(kù)進(jìn)行操作的流程:
•連接數(shù)據(jù)庫(kù)
•發(fā)送數(shù)據(jù)請(qǐng)求,即傳統(tǒng)的CRUD指令
•返回操作結(jié)果集

JDBC中常用的對(duì)象包括:
•ConnectionManager
•Connection
•Statement
•CallableStatement
•PreparedStatement
•ResultSet
•SavePoint
一個(gè)簡(jiǎn)單示例
我們來(lái)看下面一個(gè)簡(jiǎn)單的示例,它使用JDK自帶的Derby數(shù)據(jù)庫(kù),創(chuàng)建一張表,插入一些記錄,然后將記錄返回:

復(fù)制代碼 代碼如下:

一個(gè)簡(jiǎn)單的JDBC示例
 private static void test1() throws SQLException
 {
     String driver = "org.apache.derby.jdbc.EmbeddedDriver";
     String dbURL = "jdbc:derby:EmbeddedDB;create=true";

     Connection con = null;
     Statement st = null;
     try
     {
         Class.forName(driver);
         con = DriverManager.getConnection(dbURL);
         st = con.createStatement();
         st.execute("create table foo(ID INT NOT NULL, NAME VARCHAR(30))");
         st.executeUpdate("insert into foo(ID,NAME) values(1, 'Zhang San')");

         ResultSet rs = st.executeQuery("select ID,NAME from foo");

         while(rs.next())
         {
             int id = rs.getInt("ID");
             String name = rs.getString("NAME");
             System.out.println("ID=" + id + "; NAME=" + name);
         }
     }
     catch(Exception ex)
     {
         ex.printStackTrace();
     }
     finally
     {
         if (st != null) st.close();
         if (con != null) con.close();
     }
 }

如何建立數(shù)據(jù)庫(kù)連接
上面的示例代碼中,建立數(shù)據(jù)庫(kù)連接的部分如下:
復(fù)制代碼 代碼如下:

String driver = "org.apache.derby.jdbc.EmbeddedDriver";
String dbURL = "jdbc:derby:EmbeddedDB;create=true";

Class.forName(driver);
con = DriverManager.getConnection(dbURL);


建立數(shù)據(jù)庫(kù)連接的過(guò)程,可以分為兩步:

1)加載數(shù)據(jù)庫(kù)驅(qū)動(dòng),即上文中的driver以及Class.forName(dirver)

2)定位數(shù)據(jù)庫(kù)連接字符串, 即dbURL以及DriverManager.getConnection(dbURL)

不同的數(shù)據(jù)庫(kù),對(duì)應(yīng)的dirver和dbURL不同,但加載驅(qū)動(dòng)和建立連接的方式是相同的,即只需要修改上面driver和dbURL的值就可以了。

自動(dòng)加載數(shù)據(jù)庫(kù)驅(qū)動(dòng)
如果我們每次建立連接時(shí),都要使用Class.forName(...)來(lái)手動(dòng)加載數(shù)據(jù)庫(kù)驅(qū)動(dòng),這樣會(huì)很麻煩,我們可以通過(guò)配置文件的方式,來(lái)保存數(shù)據(jù)庫(kù)驅(qū)動(dòng)的信息。

我們可以在classpath中,即編譯出來(lái)的.class的存放路徑,添加如下文件:

復(fù)制代碼 代碼如下:

META-INF\services\java.sql.Driver

對(duì)應(yīng)的內(nèi)容就是JDBC驅(qū)動(dòng)的全路徑,也就是上面driver變量的值:
復(fù)制代碼 代碼如下:

org.apache.derby.jdbc.EmbeddedDriver

接下來(lái),我們?cè)诔绦蛑校筒恍枰亠@示的用Class.forName(...)來(lái)加載驅(qū)動(dòng)了,它會(huì)被自動(dòng)加載進(jìn)來(lái),當(dāng)我們的數(shù)據(jù)庫(kù)發(fā)生變化時(shí),只需要修改這個(gè)文件就可以了,例如當(dāng)我們的數(shù)據(jù)庫(kù)由Derby變?yōu)镸ySQL時(shí),只需要將上述的配置修改為:
復(fù)制代碼 代碼如下:

com.mysql.jdbc.Driver

但是,需要注意一點(diǎn),這里只是配置了JDBC驅(qū)動(dòng)的全路徑,并沒(méi)有包含jar文件的信息,因此,我們還是需要將包含該驅(qū)動(dòng)的jar文件手動(dòng)的放置到程序的classpath中。

JDBC中的基本操作
對(duì)于數(shù)據(jù)庫(kù)操作來(lái)說(shuō),CRUD操作應(yīng)該是最常見(jiàn)的操作了, 即我們常說(shuō)的增、刪、查、改。

JDBC是使用Statement和ResultSet來(lái)完成這些操作的。

如何實(shí)現(xiàn)CRUD
下面是一個(gè)實(shí)現(xiàn)CRUD的示例:

復(fù)制代碼 代碼如下:

JDBC實(shí)現(xiàn)基本的CRUD示例
 private static void insertTest() throws SQLException
 {
     String dbURL = "jdbc:mysql://localhost/test";
     Connection con = DriverManager.getConnection(dbURL, "root", "123");
     Statement st = con.createStatement();
     st.execute("insert into user(ID,NAME) values(1, 'Zhang San')");
     st.execute("insert into user(ID,NAME) values(2, 'Li Si')");
     st.execute("insert into user(ID,NAME) values(3, 'Wang Wu')");
     System.out.println("=====insert test=====");
     showUser(st);
     st.close();
     con.close();
 }

 private static void deleteTest() throws SQLException
 {
     String dbURL = "jdbc:mysql://localhost/test";
     Connection con = DriverManager.getConnection(dbURL, "root", "123");
     Statement st = con.createStatement();
     st.execute("delete from user where ID=3");
     System.out.println("=====delete test=====");
     showUser(st);
     st.close();
     con.close();
 }

 private static void updateTest() throws SQLException
 {
     String dbURL = "jdbc:mysql://localhost/test";
     Connection con = DriverManager.getConnection(dbURL, "root", "123");
     Statement st = con.createStatement();
     st.executeUpdate("update user set NAME='TEST' where ID=2");
     System.out.println("=====update test=====");
     showUser(st);
     st.close();
     con.close();
 }

 private static void showUser(Statement st) throws SQLException
 {
     ResultSet rs = st.executeQuery("select ID, NAME from user");
     while(rs.next())
     {
         int id = rs.getInt("ID");
         String name = rs.getString("NAME");
         System.out.println("ID:" + id + "; NAME=" + name);
     }
     rs.close();
 }

我們順序調(diào)用上面的測(cè)試方法:
復(fù)制代碼 代碼如下:

insertTest();
deleteTest();
updateTest();

執(zhí)行結(jié)果如下:
復(fù)制代碼 代碼如下:

=====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方法會(huì)把user表中的所有記錄打印出來(lái)。

如何調(diào)用存儲(chǔ)過(guò)程
存儲(chǔ)過(guò)程是做數(shù)據(jù)庫(kù)開(kāi)發(fā)時(shí)經(jīng)常使用的技術(shù),它可以通過(guò)節(jié)省編譯時(shí)間的方式來(lái)提升系統(tǒng)性能,我們這里的示例使用MySQL數(shù)據(jù)庫(kù)。

如何調(diào)用不帶參數(shù)的存儲(chǔ)過(guò)程
假設(shè)我們現(xiàn)在有一個(gè)簡(jiǎn)單的存儲(chǔ)過(guò)程,它只是返回user表中的所有記錄,存儲(chǔ)過(guò)程如下:

復(fù)制代碼 代碼如下:

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

我們可以使用CallableStatement來(lái)調(diào)用存儲(chǔ)過(guò)程:
復(fù)制代碼 代碼如下:

調(diào)用存儲(chǔ)過(guò)程示例一
 private static void execStoredProcedureTest() throws SQLException
 {
     String dbURL = "jdbc:mysql://localhost/test";
     Connection con = DriverManager.getConnection(dbURL, "root", "123");
     CallableStatement cst = con.prepareCall("call GetUser()");
     ResultSet rs = cst.executeQuery();
     while(rs.next())
     {
         int id = rs.getInt("ID");
         String name = rs.getString("NAME");
         System.out.println("ID:" + id + "; NAME=" + name);
     }
     rs.close();
     cst.close();
     con.close();
 }

它的執(zhí)行結(jié)果如下:
復(fù)制代碼 代碼如下:

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

如何調(diào)用帶參數(shù)的存儲(chǔ)過(guò)程
MySQL的存儲(chǔ)過(guò)程中的參數(shù)分為三種:in/out/inout,我們可以把in看做入力參數(shù),out看做出力參數(shù),JDBC對(duì)這兩種類(lèi)型的參數(shù)設(shè)置方式不同:

1)in, JDBC使用類(lèi)似于cst.set(1, 10)的方式來(lái)設(shè)置

2)out,JDBC使用類(lèi)似于cst.registerOutParameter(2, Types.VARCHAR);的方式來(lái)設(shè)置

我們來(lái)看一個(gè)in參數(shù)的示例,假設(shè)我們希望返回ID為特定值的user信息,存儲(chǔ)過(guò)程如下:

復(fù)制代碼 代碼如下:

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

Java的調(diào)用代碼如下:
復(fù)制代碼 代碼如下:

JDBC調(diào)用存儲(chǔ)過(guò)程示例二
 private static void execStoredProcedureTest2(int id) throws SQLException
 {
     String dbURL = "jdbc:mysql://localhost/test";
     Connection con = DriverManager.getConnection(dbURL, "root", "123");
     CallableStatement cst = con.prepareCall("call GetUserByID(?)");
     cst.setInt(1, id);
     ResultSet rs = cst.executeQuery();
     while(rs.next())
     {
         String name = rs.getString("NAME");
         System.out.println("ID:" + id + "; NAME=" + name);
     }
     rs.close();
     cst.close();
     con.close();
 }

我們執(zhí)行下面的語(yǔ)句:
復(fù)制代碼 代碼如下:

execStoredProcedureTest2(1);

結(jié)果如下:
復(fù)制代碼 代碼如下:

ID:1; NAME=Zhang San

對(duì)于out類(lèi)型的參數(shù),調(diào)用方式類(lèi)似,不再贅述。

獲取數(shù)據(jù)庫(kù)以及結(jié)果集的metadata信息
在JDBC中,我們不僅能夠?qū)?shù)據(jù)進(jìn)行操作,我們還能獲取數(shù)據(jù)庫(kù)以及結(jié)果集的元數(shù)據(jù)信息,例如數(shù)據(jù)庫(kù)的名稱(chēng)、驅(qū)動(dòng)信息、表信息;結(jié)果集的列信息等。

獲取數(shù)據(jù)庫(kù)的metadata信息
我們可以通過(guò)connection.getMetaData方法來(lái)獲取數(shù)據(jù)庫(kù)的元數(shù)據(jù)信息,它的類(lèi)型是DatabaseMetaData。

復(fù)制代碼 代碼如下:

獲取數(shù)據(jù)庫(kù)的元數(shù)據(jù)信息
 private static void test1() throws SQLException
 {
     String dbURL = "jdbc:mysql://localhost/mysql";
     Connection con = DriverManager.getConnection(dbURL, "root", "123");

     DatabaseMetaData dbmd = con.getMetaData();

     System.out.println("數(shù)據(jù)庫(kù):" + dbmd.getDatabaseProductName() + " " + dbmd.getDatabaseProductVersion());
     System.out.println("驅(qū)動(dòng)程序:" + dbmd.getDriverName() + " " + dbmd.getDriverVersion());

     ResultSet rs = dbmd.getTables(null, null, null, null);
     System.out.println(String.format("|%-26s|%-9s|%-9s|%-9s|", "表名稱(chēng)","表類(lèi)別","表類(lèi)型","表模式"));       
     while(rs.next())
     {
         System.out.println(String.format("|%-25s|%-10s|%-10s|%-10s|",
                 rs.getString("TABLE_NAME"),rs.getString("TABLE_CAT"),
                 rs.getString("TABLE_TYPE"), rs.getString("TABLE_SCHEM")));
     }
 }

這里我們使用的數(shù)據(jù)庫(kù)是MySQL中自帶的默認(rèn)數(shù)據(jù)庫(kù):mysql,它會(huì)記錄整個(gè)數(shù)據(jù)庫(kù)服務(wù)器中的一些信息。上述代碼執(zhí)行結(jié)果如下:
復(fù)制代碼 代碼如下:

數(shù)據(jù)庫(kù):MySQL 5.5.28
驅(qū)動(dòng)程序:MySQL-AB JDBC Driver mysql-connector-java-5.0.4 ( $Date: 2006-10-19 17:47:48 +0200 (Thu, 19 Oct 2006) $, $Revision: 5908 $ )
|表名稱(chēng)                       |表類(lèi)別      |表類(lèi)型      |表模式      |
|columns_priv             |mysql     |TABLE     |null      |
|db                       |mysql     |TABLE     |null      |
|event                    |mysql     |TABLE     |null      |
|func                     |mysql     |TABLE     |null      |
。。。

由于mysql中表比較多,上述結(jié)果只截取了一部分。

獲取結(jié)果集的元數(shù)據(jù)信息
我們可以通過(guò)使用resultset.getMetaData方法來(lái)獲取結(jié)果集的元數(shù)據(jù)信息,它的類(lèi)型是ResultSetMetaData。

復(fù)制代碼 代碼如下:

獲取結(jié)果集的元數(shù)據(jù)信息
 private static void test2() throws SQLException
 {
     String dbURL = "jdbc:mysql://localhost/test";
     Connection con = DriverManager.getConnection(dbURL, "root", "123");
     Statement st = con.createStatement();
     ResultSet rs = st.executeQuery("select ID, NAME from user");
     ResultSetMetaData rsmd = rs.getMetaData();
     for (int i = 1; i <= rsmd.getColumnCount(); i++)
     {
         System.out.println("Column Name:" + rsmd.getColumnName(i) + "; Column Type:" + rsmd.getColumnTypeName(i));
     }
 }

它的執(zhí)行結(jié)果如下:
復(fù)制代碼 代碼如下:

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

可以看到,它返回類(lèi)結(jié)果集中每一列的名稱(chēng)和類(lèi)型。

基于ResultSet的操作
當(dāng)我們需要對(duì)數(shù)據(jù)庫(kù)進(jìn)行修改時(shí),除了上述通過(guò)Statement完成操作外,我們也可以借助ResultSet來(lái)完成。

需要注意的是,在這種情況下,我們定義Statement時(shí),需要添加參數(shù)。

Statement構(gòu)造函數(shù)可以包含3個(gè)參數(shù):

•resultSetType,它的取值包括:ResultSet.TYPE_FORWARD_ONLY、ResultSet.TYPE_SCROLL_INSENSITIVE 或 ResultSet.TYPE_SCROLL_SENSITIVE,默認(rèn)情況下,該參數(shù)的值是ResultSet.TYPE_FORWARD_ONLY。
•resultSetConcurrency,它的取值包括:ResultSet.CONCUR_READ_ONLY 或 ResultSet.CONCUR_UPDATABLE,默認(rèn)情況下,該參數(shù)的值是ResultSet.CONCUR_READ_ONLY。
•resultSetHoldability,它的取值包括:ResultSet.HOLD_CURSORS_OVER_COMMIT 或 ResultSet.CLOSE_CURSORS_AT_COMMIT。
為了使得ResultSet能夠?qū)?shù)據(jù)進(jìn)行操作我們需要:

•將resultSetType設(shè)置為ResultSet.TYPE_SCROLL_SENSITIVE。
•將resultSetConcurrency設(shè)置為ResultSet.CONCUR_UPDATABLE。
在通過(guò)ResultSet對(duì)數(shù)據(jù)進(jìn)行調(diào)整的過(guò)程中,下面方法可能會(huì)被調(diào)用:

•resultset.last()
•resultset.first()
•resultset.moveToInsertRow()
•resultset.absolute()
•resultset.setxxx()
•resultset.updateRow()
•resultset.insertRow()
下面是一個(gè)通過(guò)ResultSet對(duì)數(shù)據(jù)進(jìn)行增、刪、改的示例:

復(fù)制代碼 代碼如下:

通過(guò)ResultSet對(duì)數(shù)據(jù)進(jìn)行增、刪、改
 private static void getResultCount() throws SQLException
 {
     System.out.println("=====Result Count=====");
     String dbURL = "jdbc:mysql://localhost/test";
     Connection con = DriverManager.getConnection(dbURL, "root", "123");
     Statement st = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY, ResultSet.CLOSE_CURSORS_AT_COMMIT);
     ResultSet rs = st.executeQuery("select * from user");
     rs.last();
     System.out.println("返回結(jié)果的條數(shù):"+ rs.getRow());
     rs.first();

     rs.close();
     st.close();
     con.close();
 }

 private static void insertDataToResultSet() throws SQLException
 {
     System.out.println("=====Insert=====");
     String dbURL = "jdbc:mysql://localhost/test";
     Connection con = DriverManager.getConnection(dbURL, "root", "123");
     Statement st = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
     ResultSet rs = st.executeQuery("select ID,NAME from user");
     rs.moveToInsertRow();
     rs.updateInt(1, 4);
     rs.updateString(2, "Xiao Ming");
     rs.insertRow();
     showUser(st);

     rs.close();
     st.close();
     con.close();
 }

 private static void updateDataToResultSet() throws SQLException
 {
     System.out.println("=====Update=====");
     String dbURL = "jdbc:mysql://localhost/test";
     Connection con = DriverManager.getConnection(dbURL, "root", "123");
     Statement st = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
     ResultSet rs = st.executeQuery("select * from user");
     rs.last();
     int count = rs.getRow();
     rs.first();
     rs.absolute(count);
     rs.updateString(2, "Xiao Qiang");
     rs.updateRow();
     showUser(st);

     rs.close();
     st.close();
     con.close();
 }

 private static void delDataFromResultSet() throws SQLException
 {
     System.out.println("=====Delete=====");
     String dbURL = "jdbc:mysql://localhost/test";
     Connection con = DriverManager.getConnection(dbURL, "root", "123");
     Statement st = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE, ResultSet.CLOSE_CURSORS_AT_COMMIT);
     ResultSet rs = st.executeQuery("select * from user");
     rs.last();
     int count = rs.getRow();
     rs.first();
     rs.absolute(count);
     rs.deleteRow();
     showUser(st);

     rs.close();
     st.close();
     con.close();
 }

分別調(diào)用上述方法:
復(fù)制代碼 代碼如下:

getResultCount();
insertDataToResultSet();
updateDataToResultSet();
delDataFromResultSet();

執(zhí)行結(jié)果如下:
復(fù)制代碼 代碼如下:

=====Result Count=====
返回結(jié)果的條數(shù):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

可以看到我們對(duì)ID為4的記錄進(jìn)行了插入、更新和刪除操作。

預(yù)處理以及批處理
預(yù)處理和批處理都是用來(lái)提升系統(tǒng)性能的方式,一種是利用數(shù)據(jù)庫(kù)的緩存機(jī)制,一種是利用數(shù)據(jù)庫(kù)一次執(zhí)行多條語(yǔ)句的方式。

預(yù)處理
數(shù)據(jù)庫(kù)服務(wù)器接收到Statement后,一般會(huì)解析Statement、分析是否有語(yǔ)法錯(cuò)誤、定制最優(yōu)的執(zhí)行計(jì)劃,這個(gè)過(guò)程可能會(huì)降低系統(tǒng)的性能。一般的數(shù)據(jù)庫(kù)服務(wù)器都這對(duì)這種情況,設(shè)計(jì)了緩存機(jī)制,當(dāng)數(shù)據(jù)庫(kù)接收到指令時(shí),如果緩存中已經(jīng)存在,那么就不再解析,而是直接運(yùn)行。

這里相同的指令是指sql語(yǔ)句完全一樣,包括大小寫(xiě)。

JDBC使用PreparedStatement來(lái)完成預(yù)處理:

復(fù)制代碼 代碼如下:

預(yù)處理示例
 private static void test1() throws SQLException
 {
     System.out.println("=====Insert a single record by PreparedStatement=====");
     String dbURL = "jdbc:mysql://localhost/test";
     Connection con = DriverManager.getConnection(dbURL, "root", "123");
     PreparedStatement pst = con.prepareStatement("insert into user(id,name) values(?,?)");
     pst.setInt(1, 5);
     pst.setString(2, "Lei Feng");
     pst.executeUpdate();
     showUser(pst);
     pst.close();
     con.close();
 }

執(zhí)行結(jié)果如下:
復(fù)制代碼 代碼如下:

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

批處理
批處理是利用數(shù)據(jù)庫(kù)一次執(zhí)行多條語(yǔ)句的機(jī)制來(lái)提升性能,這樣可以避免多次建立連接帶來(lái)的性能損失。

批處理使用Statement的addBatch來(lái)添加指令,使用executeBatch方法來(lái)一次執(zhí)行多條指令:

復(fù)制代碼 代碼如下:

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

執(zhí)行結(jié)果如下:
復(fù)制代碼 代碼如下:

=====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

預(yù)處理和批處理相結(jié)合
我們可以把預(yù)處理和批處理結(jié)合起來(lái),利用數(shù)據(jù)庫(kù)的緩存機(jī)制,一次執(zhí)行多條語(yǔ)句:
復(fù)制代碼 代碼如下:

預(yù)處理和批處理相結(jié)合的示例
 private static void test3() throws SQLException
 {
     System.out.println("=====Insert multiple records by PreparedStatement & Batch=====");
     String dbURL = "jdbc:mysql://localhost/test";
     Connection con = DriverManager.getConnection(dbURL, "root", "123");
     PreparedStatement pst = con.prepareStatement("insert into user(id,name) values(?,?)");
     pst.setInt(1, 9);
     pst.setString(2, "Xiao Zhang");
     pst.addBatch();
     pst.setInt(1, 10);
     pst.setString(2, "Xiao Liu");
     pst.addBatch();
     pst.setInt(1, 11);
     pst.setString(2, "Xiao Zhao");
     pst.addBatch();
     pst.executeBatch();
     showUser(pst);
     pst.close();
     con.close();
 }

執(zhí)行結(jié)果如下:
復(fù)制代碼 代碼如下:

=====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

數(shù)據(jù)庫(kù)事務(wù)
談到數(shù)據(jù)庫(kù)開(kāi)發(fā),事務(wù)是一個(gè)不可回避的話題,JDBC默認(rèn)情況下,是每一步都自動(dòng)提交的,我們可以通過(guò)設(shè)置connection.setAutoCommit(false)的方式來(lái)強(qiáng)制關(guān)閉自動(dòng)提交,然后通過(guò)connection.commit()和connection.rollback()來(lái)實(shí)現(xiàn)事務(wù)提交和回滾。

簡(jiǎn)單的數(shù)據(jù)庫(kù)事務(wù)
下面是一個(gè)簡(jiǎn)單的數(shù)據(jù)庫(kù)事務(wù)的示例:

復(fù)制代碼 代碼如下:

簡(jiǎn)單的數(shù)據(jù)庫(kù)事務(wù)示例
 private static void transactionTest1() throws SQLException
 {
     System.out.println("=====Simple Transaction test=====");
     String dbURL = "jdbc:mysql://localhost/test";
     Connection con = DriverManager.getConnection(dbURL, "root", "123");
     Statement st = con.createStatement();
     try
     {
         con.setAutoCommit(false);
         st.executeUpdate("insert into user(id,name) values(12, 'Xiao Li')");
         con.commit();
     }
     catch(Exception ex)
     {
         ex.printStackTrace();
         con.rollback();
     }
     finally
     {
         con.setAutoCommit(true);
         showUser(st);
         if (st != null) st.close();
         if (con != null) con.close();
     }
 }

連續(xù)執(zhí)行上述方法兩次,我們可以得出下面的結(jié)果:
復(fù)制代碼 代碼如下:

=====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)

可以看到,第一次調(diào)用時(shí),操作成功,事務(wù)提交,向user表中插入了一條記錄;第二次調(diào)用時(shí),發(fā)生主鍵沖突異常,事務(wù)回滾。

帶有SavePoint的事務(wù)
當(dāng)我們的事務(wù)操作中包含多個(gè)處理,但我們有時(shí)希望一些操作完成后可以先提交,這樣可以避免整個(gè)事務(wù)的回滾。JDBC使用SavePoint來(lái)實(shí)現(xiàn)這一點(diǎn)。

復(fù)制代碼 代碼如下:

帶有SavePoint的事務(wù)示例
 private static void transactionTest2() throws SQLException
 {
     System.out.println("=====Simple Transaction test=====");
     String dbURL = "jdbc:mysql://localhost/test";
     Connection con = DriverManager.getConnection(dbURL, "root", "123");
     Statement st = con.createStatement();
     Savepoint svpt = null;
     try
     {
         con.setAutoCommit(false);
         st.executeUpdate("insert into user(id,name) values(13, 'Xiao Li')");
         st.executeUpdate("insert into user(id,name) values(14, 'Xiao Wang')");
         svpt = con.setSavepoint("roll back to here");
         st.executeUpdate("insert into user(id,name) values(15, 'Xiao Zhao')");
         st.executeUpdate("insert into user(id,name) values(13, 'Xiao Li')");
         con.commit();
     }
     catch(Exception ex)
     {
         ex.printStackTrace();
         con.rollback(svpt);
     }
     finally
     {
         con.setAutoCommit(true);
         showUser(st);
         if (st != null) st.close();
         if (con != null) con.close();
     }
 }

執(zhí)行結(jié)果如下:
復(fù)制代碼 代碼如下:

=====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

可以看到最終事務(wù)報(bào)出了主鍵沖突異常,事務(wù)回滾,但是依然向數(shù)據(jù)庫(kù)中插入了ID為13和14的記錄。

另外,在確定SavePoint后,ID為15的記錄并沒(méi)有被插入,它是通過(guò)事務(wù)進(jìn)行了回滾。

相關(guān)文章

  • Springboot?格式化LocalDateTime的方法

    Springboot?格式化LocalDateTime的方法

    這篇文章主要介紹了Springboot格式化LocalDateTime的相關(guān)知識(shí),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-05-05
  • Spring框架初始化解析

    Spring框架初始化解析

    這篇文章主要介紹了Spring框架初始化解析,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-11-11
  • Java?BigDecimal正確用法詳解

    Java?BigDecimal正確用法詳解

    Java在java.math包中提供的API類(lèi)BigDecimal,用來(lái)對(duì)超過(guò)16位有效位的數(shù)進(jìn)行精確的運(yùn)算。雙精度浮點(diǎn)型變量double可以處理16位有效數(shù),但在實(shí)際應(yīng)用中,可能需要對(duì)更大或者更小的數(shù)進(jìn)行運(yùn)算和處理
    2022-10-10
  • window下安裝和配置maven環(huán)境

    window下安裝和配置maven環(huán)境

    這篇文章主要為大家詳細(xì)介紹了window下安裝和配置maven環(huán)境的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-03-03
  • Java JVM原理與調(diào)優(yōu)_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    Java JVM原理與調(diào)優(yōu)_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    JVM是Java Virtual Machine(Java虛擬機(jī))的縮寫(xiě),JVM是一種用于計(jì)算設(shè)備的規(guī)范,它是一個(gè)虛構(gòu)出來(lái)的計(jì)算機(jī),是通過(guò)在實(shí)際的計(jì)算機(jī)上仿真模擬各種計(jì)算機(jī)功能來(lái)實(shí)現(xiàn)的。下面通過(guò)本文給大家介紹jvm原理與調(diào)優(yōu)相關(guān)知識(shí),感興趣的朋友一起學(xué)習(xí)吧
    2017-04-04
  • java中使用雙向鏈表實(shí)現(xiàn)貪吃蛇程序源碼分享

    java中使用雙向鏈表實(shí)現(xiàn)貪吃蛇程序源碼分享

    這篇文章主要介紹了java中使用雙向鏈表實(shí)現(xiàn)貪吃蛇程序源碼分享,本文直接給出了實(shí)現(xiàn)代碼,需要的朋友可以參考下
    2015-03-03
  • Java一些常見(jiàn)的出錯(cuò)異常處理方法總結(jié)

    Java一些常見(jiàn)的出錯(cuò)異常處理方法總結(jié)

    下面小編就為大家?guī)?lái)一篇Java一些常見(jiàn)的出錯(cuò)異常處理方法總結(jié)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-06-06
  • 兩個(gè)例子了解java中的回調(diào)機(jī)制

    兩個(gè)例子了解java中的回調(diào)機(jī)制

    這篇文章主要介紹了Java中回調(diào)機(jī)制的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下
    2021-02-02
  • 基于slf4j日志MDC輸出格式的問(wèn)題

    基于slf4j日志MDC輸出格式的問(wèn)題

    這篇文章主要介紹了基于slf4j日志MDC輸出格式的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • 關(guān)于@Value取值為NULL的解決方案

    關(guān)于@Value取值為NULL的解決方案

    這篇文章主要介紹了關(guān)于@Value取值為NULL的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-07-07

最新評(píng)論