天天看點

老調重彈:JDBC系列 之 <JDBC層次結構和基本構成>

前言

       最近在研究Mybatis架構,由于該架構基于JDBC,想要很好地了解和學習Mybatis,必須要對JDBC有較深入的了解。是以便把JDBC 這個東東翻出來,老調重彈,好好總結一番,作為自己的筆記,也是給讀者一個參考~~~本篇博文是我的上篇博文 老調重彈:JDBC系列 之 <驅動加載原理全面解析> 的續文,主要梳理一下JDBC的層次結構和基本構成。以下是本文的組織内容(使用者可以點選上面的目錄欄檢視):
老調重彈:JDBC系列 之 &lt;JDBC層次結構和基本構成&gt;

JDBC的層次結構

總體而言,JDBC包含以下幾大角色 : Driver、DriverManager、Connection、Statement、ResultSet。這幾大角色之間的層次關系如下圖所示:
老調重彈:JDBC系列 之 &lt;JDBC層次結構和基本構成&gt;

      其中,DriverManager 和 Driver 這兩個角色已經在我的上一篇文章:老調重彈:JDBC系列 之 <驅動加載原理全面解析>闡述過了,讀者可以點選檢視。

Connection:Driver 或者 DriverManager根據連接配接的url 和參數資訊建立Connection執行個體,用來維持和資料庫的資料通信,如果沒有銷毀或者調用close()對象,此對象和資料庫的對象會一直保持連接配接;

Statement:Connection建立Statement對象,表示需要執行的sql語句或者存儲過程;

ResultSet: 表示Statement執行完SQL語句後傳回的結果集。

基本構成分析

Connection角色

      Connection表示與特定資料庫的連接配接,可以擷取到資料庫的一些資訊,這些資訊包括:其表資訊,應該支援的SQL文法,資料庫内有什麼存儲過程,此連結功能的資訊等等。
老調重彈:JDBC系列 之 &lt;JDBC層次結構和基本構成&gt;
      在一般實際使用情況下,我們關注的Connection的功能有以下幾點:
1.建立可以執行sql語句或者存儲過程的對象statement,用來和資料庫進行互動;
比如,以下代碼建立了幾種不同類型的Statement:
//加載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);
           
2. 控制sql語句的事務;

        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();
			}
           
3.擷取資料庫連接配接的中繼資料,即資料庫的整體綜合資訊。
連接配接的資料庫整體資訊被封裝在了一個 DatabaseMetaData類型的對象上,可以通過以下代碼獲得:
DatabaseMetaData databaseMetaData = connection.getMetaData();
           
具體DatabaseMetaData内包含了什麼資訊,請檢視 JDK 的API對DatabaseMetaData的描述。

Statement角色

      Statement 的功能在于根據傳入的sql語句,将傳入sql經過整理組合成資料庫能夠識别的sql語句(對于靜态的sql語句,不需要整理組合;而對于預編譯sql語句和批量語句,則需要整理),然後傳遞sql請求,之後會得到傳回的結果。對于查詢sql,結果會以ResultSet的形式傳回。
老調重彈:JDBC系列 之 &lt;JDBC層次結構和基本構成&gt;
SQL語句可以分為增删改查(CRUD,Create,Read,Update,Delete)四種形式,JDBC 從對資料更新與否的角度上看,将上面的四種形式分為兩類:查詢類别和更新類别。即:

查詢類别:select 語句

更新類别:Insert 、update、delete語句

對應地,Statement執行sql的幾種形式:
1. 對sql語句類型不進行區分,執行sql語句的方法
statement提供了execute(String sql)方法支援此種形式,定義如下:

 boolean

execute(String sql)

          執行給定的 SQL 語句,該語句可能傳回多個結果。

如果是執行的sql是查詢類型的select語句,此方法會傳回true,需要自己再調用 statement.getResultSet() 方法來擷取 Resultset結果集;

如果是執行的更新類的sql語句如 update,delete,insert語句,此方法會傳回false,自己調用statement.getUpdateCount()  傳回sql語句影響的行數。

2. 對查詢類型的sql語句的執行方法
statement提供了executeQuery(String sql)方法支援此形式,定義如下:

 ResultSet

executeQuery(String sql)

          執行給定的 SQL 語句,該語句傳回單個

ResultSet

對象。
3. 對更新類的sql語句 的執行方法
statement提供了executeQuery(String sql)方法支援此形式,定義如下:

 int

executeUpdate(String sql)

          執行給定 SQL 語句,該語句可能為

INSERT

UPDATE

DELETE

語句,或者不傳回任何内容的 SQL 語句(如 SQL DDL 語句)。
4.批量sql的執行方法
有時候需要将一些sql語句一起送出給資料庫,批量執行,statement提供了一些方法,對批量sql的支援:

 void

addBatch(String sql)

          将給定的 SQL 指令添加到此

Statement

對象的目前指令清單中。

 int[]

executeBatch()

          将一批指令送出給資料庫來執行,如果全部指令執行成功,則傳回更新計數組成的數組。
這裡隻讨論一般性的Statement,不包含其子接口PreparedStatement和CallableStatement,這兩個類型的Statement将會在後續的   老調重彈:JDBC系列 中繼續讨論。

ResultSet角色

      當Statement查詢sql執行後,會得到ResultSet對象,ResultSet對象是sql語句查詢的結果,作為資料庫結果的映射,其映射關系如下圖所示。ResultSet對從資料庫傳回的結果進行了封裝,使用疊代器的模式逐條取出結果集中的記錄。其周遊結果集的基本形式如下:
while(resultSet.next())
			{
				//傳入列明或者列索引擷取記錄中對應列的值
				resultSet.getXXX(param);
			}
           
老調重彈:JDBC系列 之 &lt;JDBC層次結構和基本構成&gt;
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結果集的中繼資料資訊
元資訊是指關于

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系列 之 &lt;JDBC層次結構和基本構成&gt;

-------------------------------------------------------------------------------------------------------------------------------------------------------------    

        以上是本文 老調重彈:JDBC系列 之 <JDBC層次結構和基本構成> 的全部内容,以上是自己心得,并非權威,如有不妥或者此錯誤之處,歡迎讀者批評和斧正!  歡迎關注我的下一篇博文: 老調重彈:JDBC系列 之 <JDBC 事務>