自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java回顧之JDBC

開發(fā) 后端
將JDBC的各種細節(jié)操作整理打包,生成易于操作的ORM框架來進行數(shù)據(jù)庫各項操作。但理解JDBC訪問步驟,對于理解Java數(shù)據(jù)操作流程很有幫助。

 概述

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

  JDBC的全稱是Java Database Connectivity。

  JDBC對數(shù)據(jù)庫進行操作的流程:

  • 連接數(shù)據(jù)庫
  • 發(fā)送數(shù)據(jù)請求,即傳統(tǒng)的CRUD指令
  • 返回操作結(jié)果集

  JDBC中常用的對象包括:

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

  一個簡單示例

  我們來看下面一個簡單的示例,它使用JDK自帶的Derby數(shù)據(jù)庫,創(chuàng)建一張表,插入一些記錄,然后將記錄返回:

  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.  } 

  如何建立數(shù)據(jù)庫連接

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

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

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

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

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

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

  自動加載數(shù)據(jù)庫驅(qū)動

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

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

META-INF\services\java.sql.Driver

  對應(yīng)的內(nèi)容就是JDBC驅(qū)動的全路徑,也就是上面driver變量的值:

org.apache.derby.jdbc.EmbeddedDriver

  接下來,我們在程序中,就不需要再顯示的用Class.forName(...)來加載驅(qū)動了,它會被自動加載進來,當我們的數(shù)據(jù)庫發(fā)生變化時,只需要修改這個文件就可以了,例如當我們的數(shù)據(jù)庫由Derby變?yōu)镸ySQL時,只需要將上述的配置修改為:

com.mysql.jdbc.Driver

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

  JDBC中的基本操作

  對于數(shù)據(jù)庫操作來說,CRUD操作應(yīng)該是最常見的操作了, 即我們常說的增、刪、查、改。

  JDBC是使用Statement和ResultSet來完成這些操作的。

  如何實現(xiàn)CRUD

  下面是一個實現(xiàn)CRUD的示例:

JDBC實現(xiàn)基本的CRUD示例

  我們順序調(diào)用上面的測試方法:

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

  執(zhí)行結(jié)果如下:

=====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表中的所有記錄打印出來。

#p#

  如何調(diào)用存儲過程

  存儲過程是做數(shù)據(jù)庫開發(fā)時經(jīng)常使用的技術(shù),它可以通過節(jié)省編譯時間的方式來提升系統(tǒng)性能,我們這里的示例使用MySQL數(shù)據(jù)庫。

  如何調(diào)用不帶參數(shù)的存儲過程

  假設(shè)我們現(xiàn)在有一個簡單的存儲過程,它只是返回user表中的所有記錄,存儲過程如下:

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

  我們可以使用CallableStatement來調(diào)用存儲過程:

調(diào)用存儲過程示例一

  它的執(zhí)行結(jié)果如下:

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

  如何調(diào)用帶參數(shù)的存儲過程

  MySQL的存儲過程中的參數(shù)分為三種:in/out/inout,我們可以把in看做入力參數(shù),out看做出力參數(shù),JDBC對這兩種類型的參數(shù)設(shè)置方式不同:

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

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

  我們來看一個in參數(shù)的示例,假設(shè)我們希望返回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的調(diào)用代碼如下:

JDBC調(diào)用存儲過程示例二

  我們執(zhí)行下面的語句:

execStoredProcedureTest2(1);

  結(jié)果如下:

ID:1; NAME=Zhang San

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

  獲取數(shù)據(jù)庫以及結(jié)果集的metadata信息

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

  獲取數(shù)據(jù)庫的metadata信息

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

獲取數(shù)據(jù)庫的元數(shù)據(jù)信息

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

數(shù)據(jù)庫:MySQL 5.5.28 驅(qū)動程序: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中表比較多,上述結(jié)果只截取了一部分。

  獲取結(jié)果集的元數(shù)據(jù)信息

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

獲取結(jié)果集的元數(shù)據(jù)信息

  它的執(zhí)行結(jié)果如下:

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

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

  基于ResultSet的操作

  當我們需要對數(shù)據(jù)庫進行修改時,除了上述通過Statement完成操作外,我們也可以借助ResultSet來完成。

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

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

  • resultSetType,它的取值包括:ResultSet.TYPE_FORWARD_ONLY、ResultSet.TYPE_SCROLL_INSENSITIVEResultSet.TYPE_SCROLL_SENSITIVE,默認情況下,該參數(shù)的值是ResultSet.TYPE_FORWARD_ONLY。
  • resultSetConcurrency,它的取值包括:ResultSet.CONCUR_READ_ONLYResultSet.CONCUR_UPDATABLE,默認情況下,該參數(shù)的值是ResultSet.CONCUR_READ_ONLY。
  • resultSetHoldability,它的取值包括:ResultSet.HOLD_CURSORS_OVER_COMMITResultSet.CLOSE_CURSORS_AT_COMMIT。

  為了使得ResultSet能夠?qū)?shù)據(jù)進行操作我們需要:

  • 將resultSetType設(shè)置為ResultSet.TYPE_SCROLL_SENSITIVE。
  • 將resultSetConcurrency設(shè)置為ResultSet.CONCUR_UPDATABLE。

  在通過ResultSet對數(shù)據(jù)進行調(diào)整的過程中,下面方法可能會被調(diào)用:

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

  下面是一個通過ResultSet對數(shù)據(jù)進行增、刪、改的示例:

通過ResultSet對數(shù)據(jù)進行增、刪、改

  分別調(diào)用上述方法:

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

  執(zhí)行結(jié)果如下:

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

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

#p#

  預(yù)處理以及批處理

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

  預(yù)處理

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

  這里相同的指令是指sql語句完全一樣,包括大小寫。

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

預(yù)處理示例

  執(zhí)行結(jié)果如下:

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

  批處理

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

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

批處理示例

  執(zhí)行結(jié)果如下:

=====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é)合起來,利用數(shù)據(jù)庫的緩存機制,一次執(zhí)行多條語句:

預(yù)處理和批處理相結(jié)合的示例

  執(zhí)行結(jié)果如下:

=====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ù)庫事務(wù)

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

  簡單的數(shù)據(jù)庫事務(wù)

  下面是一個簡單的數(shù)據(jù)庫事務(wù)的示例:

簡單的數(shù)據(jù)庫事務(wù)示例

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

=====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)用時,操作成功,事務(wù)提交,向user表中插入了一條記錄;第二次調(diào)用時,發(fā)生主鍵沖突異常,事務(wù)回滾。

  帶有SavePoint的事務(wù)

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

帶有SavePoint的事務(wù)示例

  執(zhí)行結(jié)果如下:

=====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ù)報出了主鍵沖突異常,事務(wù)回滾,但是依然向數(shù)據(jù)庫中插入了ID為13和14的記錄。

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

原文鏈接:http://www.cnblogs.com/wing011203/archive/2013/05/12/3073838.html

責(zé)任編輯:陳四芳 來源: 博客園
相關(guān)推薦

2013-05-28 10:08:41

IO輸出

2011-03-24 09:22:36

Java 7JDBC4

2018-07-24 09:38:35

JavaMySQLJDBC

2013-05-27 12:59:22

設(shè)計模式GoF

2012-04-05 10:27:49

GooglePR之賽

2009-07-22 16:07:11

Java JDBC編程

2013-01-29 09:43:33

JavaJVMJava社區(qū)

2015-09-09 10:34:58

底層網(wǎng)絡(luò)技術(shù)網(wǎng)絡(luò)技術(shù)

2021-10-11 08:51:50

JavaMailJDBCJava

2011-05-20 09:53:00

JDK7

2009-07-16 13:51:43

2009-07-22 15:58:34

JDBC調(diào)用Oracl

2009-07-07 15:59:51

2009-06-19 16:38:45

JDBC簡介J2EE

2012-08-16 09:46:05

Debian

2010-01-14 20:49:08

2010-01-15 21:47:39

2020-10-12 08:43:25

Java基礎(chǔ)知識

2009-07-20 17:41:59

Java JDBC
點贊
收藏

51CTO技術(shù)棧公眾號