前言
最近在研究Mybatis架構,由于該架構基于JDBC,想要很好地了解和學習Mybatis,必須要對JDBC有較深入的了解。是以便把JDBC 這個東東翻出來,老調重彈,好好總結一番,作為自己的筆記,也是給讀者一個參考~~~本篇博文是我的上篇博文 老調重彈:JDBC系列 之 <驅動加載原理全面解析> 的續文,主要梳理一下JDBC的層次結構和基本構成。以下是本文的組織内容(使用者可以點選上面的目錄欄檢視):
JDBC的層次結構
總體而言,JDBC包含以下幾大角色 : Driver、DriverManager、Connection、Statement、ResultSet。這幾大角色之間的層次關系如下圖所示:
其中,DriverManager 和 Driver 這兩個角色已經在我的上一篇文章:老調重彈:JDBC系列 之 <驅動加載原理全面解析>闡述過了,讀者可以點選檢視。
Connection:Driver 或者 DriverManager根據連接配接的url 和參數資訊建立Connection執行個體,用來維持和資料庫的資料通信,如果沒有銷毀或者調用close()對象,此對象和資料庫的對象會一直保持連接配接;
Statement:Connection建立Statement對象,表示需要執行的sql語句或者存儲過程;
ResultSet: 表示Statement執行完SQL語句後傳回的結果集。
基本構成分析
Connection角色
Connection表示與特定資料庫的連接配接,可以擷取到資料庫的一些資訊,這些資訊包括:其表資訊,應該支援的SQL文法,資料庫内有什麼存儲過程,此連結功能的資訊等等。在一般實際使用情況下,我們關注的Connection的功能有以下幾點:1.建立可以執行sql語句或者存儲過程的對象statement,用來和資料庫進行互動;比如,以下代碼建立了幾種不同類型的Statement:2. 控制sql語句的事務;//加載Oracle資料庫驅動 Class.forName("oracle.jdbc.driver.OracleDriver"); //根據特定的URL,傳回可以接受此URL的資料庫驅動對象 Driver driver = DriverManager.getDriver(URL); //使用資料庫驅動建立資料庫連接配接Connection會話 Connection connection = driver.connect(URL, props); //建立靜态的sql語句 Statement 對象來将 SQL 語句發送到資料庫。 Statement staticStatement= connection.createStatement(); //建立CallableStatement 對象來調用資料庫存儲過程。 CallableStatement callableStatement = connection.prepareCall(sqlString); //建立參數化的Statement對象 PreparedStatement preparedStatement = connection.prepareStatement(sqlString);
3.擷取資料庫連接配接的中繼資料,即資料庫的整體綜合資訊。Connection預設情況下,對于建立的statement執行的sql語句都是自動送出的,即在statement語句執行完後,自動執行commit操作,将結果影響到實體資料庫。為了滿足更好地事務控制需求,我們也可以手動地控制事務,手動地對statement 的sql語句執行進行送出(commit)或者復原(rollback)。
具體事務控制,請關注我的 後續博文老調重彈:JDBC系列
下面通過一個簡單的例子示範connection的事務控制:
String sqlString="insert into tableName(column1,column2) values(value1,value2)"; //加載Oracle資料庫驅動 Class.forName("oracle.jdbc.driver.OracleDriver"); //根據特定的URL,傳回可以接受此URL的資料庫驅動對象 Driver driver = DriverManager.getDriver(URL); //使用資料庫驅動建立資料庫連接配接Connection會話 connection = driver.connect(URL, props); //使用自定義的事務,要設定connection不自動送出 connection.setAutoCommit(false); //建立靜态的sql語句 Statement 對象來将 SQL 語句發送到資料庫。 Statement staticStatement= connection.createStatement(); try{ //執行插入操作 staticStatement.execute(sqlString); staticStatement.getConnection().commit();//和上面的connection等價,statement隻有一個建立自身的connection的引用 }catch(Exception e) { //有異常,則rollback staticStatement.getConnection().rollback(); }
連接配接的資料庫整體資訊被封裝在了一個 DatabaseMetaData類型的對象上,可以通過以下代碼獲得:具體DatabaseMetaData内包含了什麼資訊,請檢視 JDK 的API對DatabaseMetaData的描述。DatabaseMetaData databaseMetaData = connection.getMetaData();
Statement角色
Statement 的功能在于根據傳入的sql語句,将傳入sql經過整理組合成資料庫能夠識别的sql語句(對于靜态的sql語句,不需要整理組合;而對于預編譯sql語句和批量語句,則需要整理),然後傳遞sql請求,之後會得到傳回的結果。對于查詢sql,結果會以ResultSet的形式傳回。SQL語句可以分為增删改查(CRUD,Create,Read,Update,Delete)四種形式,JDBC 從對資料更新與否的角度上看,将上面的四種形式分為兩類:查詢類别和更新類别。即:對應地,Statement執行sql的幾種形式:查詢類别:select 語句
更新類别:Insert 、update、delete語句
1. 對sql語句類型不進行區分,執行sql語句的方法statement提供了execute(String sql)方法支援此種形式,定義如下:2. 對查詢類型的sql語句的執行方法
boolean
執行給定的 SQL 語句,該語句可能傳回多個結果。
execute(String sql)
如果是執行的sql是查詢類型的select語句,此方法會傳回true,需要自己再調用 statement.getResultSet() 方法來擷取 Resultset結果集;
如果是執行的更新類的sql語句如 update,delete,insert語句,此方法會傳回false,自己調用statement.getUpdateCount() 傳回sql語句影響的行數。
statement提供了executeQuery(String sql)方法支援此形式,定義如下:3. 對更新類的sql語句 的執行方法
ResultSet
執行給定的 SQL 語句,該語句傳回單個
executeQuery(String sql)
對象。
ResultSet
statement提供了executeQuery(String sql)方法支援此形式,定義如下:4.批量sql的執行方法
int
執行給定 SQL 語句,該語句可能為
executeUpdate(String sql)
、
INSERT
或
UPDATE
語句,或者不傳回任何内容的 SQL 語句(如 SQL DDL 語句)。
DELETE
有時候需要将一些sql語句一起送出給資料庫,批量執行,statement提供了一些方法,對批量sql的支援:這裡隻讨論一般性的Statement,不包含其子接口PreparedStatement和CallableStatement,這兩個類型的Statement将會在後續的 老調重彈:JDBC系列 中繼續讨論。
void
将給定的 SQL 指令添加到此
addBatch(String sql)
對象的目前指令清單中。
Statement
int[]
将一批指令送出給資料庫來執行,如果全部指令執行成功,則傳回更新計數組成的數組。
executeBatch()
ResultSet角色
當Statement查詢sql執行後,會得到ResultSet對象,ResultSet對象是sql語句查詢的結果,作為資料庫結果的映射,其映射關系如下圖所示。ResultSet對從資料庫傳回的結果進行了封裝,使用疊代器的模式逐條取出結果集中的記錄。其周遊結果集的基本形式如下:while(resultSet.next()) { //傳入列明或者列索引擷取記錄中對應列的值 resultSet.getXXX(param); }
ResultSet遊标的移動和定位Resultset 提供了很多遊标定位的方法,部分方法已經在下面列出:ResultSet結果集的中繼資料資訊
boolean
将光标移動到此
absolute(int row)
對象的給定行編号。
ResultSet
void
将光标移動到此
afterLast()
對象的末尾,正好位于最後一行之後。
ResultSet
void
将光标移動到此
beforeFirst()
對象的開頭,正好位于第一行之前。
ResultSet
boolean
将光标移動到此
first()
對象的第一行。
ResultSet
int
擷取目前行編号。
getRow()
boolean
擷取光标是否位于此
isAfterLast()
對象的最後一行之後。
ResultSet
boolean
擷取光标是否位于此
isBeforeFirst()
對象的第一行之前。
ResultSet
boolean
擷取光标是否位于此
isFirst()
對象的第一行。
ResultSet
boolean
擷取光标是否位于此
isLast()
對象的最後一行。
ResultSet
boolean
将光标移動到此
last()
對象的最後一行。
ResultSet
boolean
将光标從目前位置向前移一行。
next()
boolean
将光标移動到此
previous()
對象的上一行。
ResultSet
boolean
按相對行數(或正或負)移動光标。
relative(int rows)
元資訊是指關于對象中列的類型和屬性資訊的對象。可以通過以下方法擷取:
ResultSet
ResultSetMetaData
擷取此
getMetaData()
對象的列的編号、類型和屬性。
ResultSet
ResultSet.getXXX(param) 、ResultSet.updateXXX()的XXX問題
JDBC中定義了資料庫中的資料類型和java資料類型的映射,用于資料庫和Java資料類型之間的轉換。在使用ResultSet去記錄中的某一列值的時候,使用者要根據資料庫對應列的資料類型地應的java資料類型,否則的話有可能抛出異常。下圖定義了資料庫和Java類型之間的映射:
SQL JDBC/Java setXXX updateXXX VARCHAR java.lang.String setString updateString CHAR java.lang.String setString updateString LONGVARCHAR java.lang.String setString updateString BIT boolean setBoolean updateBoolean NUMERIC java.math.BigDecimal setBigDecimal updateBigDecimal TINYINT byte setByte updateByte SMALLINT short setShort updateShort INTEGER int setInt updateInt BIGINT long setLong updateLong REAL float setFloat updateFloat FLOAT float setFloat updateFloat DOUBLE double setDouble updateDouble VARBINARY byte[ ] setBytes updateBytes BINARY byte[ ] setBytes updateBytes DATE java.sql.Date setDate updateDate TIME java.sql.Time setTime updateTime TIMESTAMP java.sql.Timestamp setTimestamp updateTimestamp CLOB java.sql.Clob setClob updateClob BLOB java.sql.Blob setBlob updateBlob ARRAY java.sql.Array setARRAY updateARRAY REF java.sql.Ref SetRef updateRef STRUCT java.sql.Struct SetStruct updateStruct
JDBC工作的基本流程
一個基本的JDBC工作流程,分為以下幾步:以下是一個簡單的案例:1.加載特定資料庫驅動器實作類,并注冊驅動器(Driver會注冊到DriverManager中);
2. 根據特定的URL,傳回可以接受此URL的資料庫驅動對象Driver;
3.使用資料庫驅動 Driver 建立資料庫連接配接Connection會話;
4. 使用 Connection對象建立 用于操作sql的Statement對象;
5. statement對象 .執行 sql語句,傳回結果ResultSet 對象;
6. 處理ResultSet中的結果;
7. 關閉連接配接,釋放資源。
public class DBConnection { static final String URL = "jdbc:oracle:thin:@127.0.0.1:1521:xe"; static final String USER_NAME ="louluan"; static final String PASSWORD = "123456"; public static void main(String[] args) { connectionTest(); } public static void connectionTest(){ Connection connection = null; Statement statement = null; ResultSet resultSet = null; try { //1.加載類,并注冊驅動器(Driver會注冊到DriverManager中) //加載Oracle資料庫驅動 Class.forName("oracle.jdbc.driver.OracleDriver").newInstance(); //2.根據特定的URL,傳回可以接受此URL的資料庫驅動對象 Driver driver = DriverManager.getDriver(URL); Properties props = new Properties(); props.put("user", USER_NAME); props.put("password", PASSWORD); //3.使用資料庫驅動建立資料庫連接配接Connection會話 connection = driver.connect(URL, props); //4.獲得Statement對象 statement = connection.createStatement(); //5.執行 sql語句,傳回結果 resultSet = statement.executeQuery("select * from hr.employees"); //6.處理結果,取出資料 while(resultSet.next()) { System.out.println(resultSet.getString(2)); } //7.關閉連結,釋放資源 } catch (ClassNotFoundException e) { System.out.println("加載Oracle類失敗!"); e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ //使用完成後管理連結,釋放資源,釋放順序應該是: ResultSet ->Statement ->Connection try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } try { statement.close(); } catch (SQLException e) { e.printStackTrace(); } try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
JDBC工作時序圖
上述幾個對象之間:DriverManager或者Driver建立Connection、Connection建立Statement、Statement又獲得ResultSet,它們之間的互動序列圖如下所示:
-------------------------------------------------------------------------------------------------------------------------------------------------------------
以上是本文 老調重彈:JDBC系列 之 <JDBC層次結構和基本構成> 的全部内容,以上是自己心得,并非權威,如有不妥或者此錯誤之處,歡迎讀者批評和斧正! 歡迎關注我的下一篇博文: 老調重彈:JDBC系列 之 <JDBC 事務>