目錄html
參考連接:https://www.yiibai.com/jdbc/jdbc_quick_guide.htmljava
JDBC表明Java數據庫鏈接(Java Database Connectivity),它是用於Java編程語言和數據庫之間的數據庫無關鏈接的標準Java API,換句話說:JDBC是用於在Java語言編程中與數據庫鏈接的API。mysql
JDBC庫包括一般與數據庫使用相關,以下面提到的每一個任務的API -sql
JDBC API支持用於數據庫訪問的兩層和三層處理模型,但一般,JDBC體系結構由兩層組成:數據庫
JDBC API使用驅動程序管理器並指定數據庫的驅動程序來提供與異構數據庫的透明鏈接。編程
JDBC驅動程序管理器確保使用正確的驅動程序來訪問每一個數據源。 驅動程序管理器可以支持鏈接到多個異構數據庫的多個併發驅動程序。數組
如下是架構圖,它顯示了驅動程序管理器相對於JDBC驅動程序和Java應用程序的位置服務器
JDBC API提供如下接口和類 -架構
DriverManager
:此類管理數據庫驅動程序列表。 使用通訊子協議未來自java應用程序的鏈接請求與適當的數據庫驅動程序進行匹配。在JDBC下識別某個子協議的第一個驅動程序將用於創建數據庫鏈接。Driver
:此接口處理與數據庫服務器的通訊。咱們不多會直接與Driver
對象進行交互。 但會使用DriverManager
對象來管理這種類型的對象。 它還提取與使用Driver
對象相關的信息。Connection
:此接口具備用於聯繫數據庫的全部方法。 鏈接(Connection
)對象表示通訊上下文,即,與數據庫的全部通訊僅經過鏈接對象。Statement
:使用今後接口建立的對象將SQL語句提交到數據庫。 除了執行存儲過程以外,一些派生接口還接受參數。ResultSet
:在使用Statement
對象執行SQL查詢後,這些對象保存從數據庫檢索的數據。 它做爲一個迭代器並可移動ResultSet
對象查詢的數據。SQLException
:此類處理數據庫應用程序中發生的任何錯誤。//STEP 1. Import required packages import java.sql.*; public class FirstExample { // JDBC driver name and database URL static final String JDBC_DRIVER = "com.mysql.jdbc.Driver"; static final String DB_URL = "jdbc:mysql://localhost/emp"; // Database credentials static final String USER = "root"; static final String PASS = "123456"; public static void main(String[] args) { Connection conn = null; Statement stmt = null; try{ //STEP 2: Register JDBC driver Class.forName("com.mysql.jdbc.Driver"); //STEP 3: Open a connection System.out.println("Connecting to database..."); conn = DriverManager.getConnection(DB_URL,USER,PASS); //STEP 4: Execute a query System.out.println("Creating statement..."); stmt = conn.createStatement(); String sql; sql = "SELECT id, first, last, age FROM Employees"; ResultSet rs = stmt.executeQuery(sql); //STEP 5: Extract data from result set while(rs.next()){ //Retrieve by column name int id = rs.getInt("id"); int age = rs.getInt("age"); String first = rs.getString("first"); String last = rs.getString("last"); //Display values System.out.print("ID: " + id); System.out.print(", Age: " + age); System.out.print(", First: " + first); System.out.println(", Last: " + last); } //STEP 6: Clean-up environment rs.close(); stmt.close(); conn.close(); }catch(SQLException se){ //Handle errors for JDBC se.printStackTrace(); }catch(Exception e){ //Handle errors for Class.forName e.printStackTrace(); }finally{ //finally block used to close resources try{ if(stmt!=null) stmt.close(); }catch(SQLException se2){ }// nothing we can do try{ if(conn!=null) conn.close(); }catch(SQLException se){ se.printStackTrace(); }//end finally try }//end try System.out.println("There are so thing wrong!"); }//end main }//end FirstExample
創建JDBC鏈接所涉及的編程至關簡單。 如下是基本的四個步驟 -併發
import
語句在Java代碼開頭位置導入所需的類。DriverManager
對象的getConnection()
方法來創建實際的數據庫鏈接。Class.forName("com.mysql.jdbc.Driver"); Connection conn = null; conn = DriverManager.getConnection("jdbc:mysql://hostname:port/db_name","db_username", "db_password"); conn.close()
接口 | 推薦使用 |
---|---|
Statement |
用於對數據庫進行通用訪問,在運行時使用靜態SQL語句時頗有用。 Statement 接口不能接受參數。 |
PreparedStatement |
當計劃要屢次使用SQL語句時使用。PreparedStatement 接口在運行時接受輸入參數。 |
CallableStatement |
當想要訪問數據庫存儲過程時使用。CallableStatement 接口也能夠接受運行時輸入參數。 |
在使用Statement
對象執行SQL語句以前,須要使用Connection
對象的createStatement()
方法建立一個Statement
對象,如如下示例所示:
Statement stmt = null; try { stmt = conn.createStatement( ); . . . } catch (SQLException e) { . . . } finally { stmt.close(); }
在建立Statement
對象後,可使用它來執行一個SQL語句,它有三個執行方法能夠執行。它們分別是 -
boolean execute (String SQL)
: 若是能夠檢索到ResultSet
對象,則返回一個布爾值true
; 不然返回false
。使用此方法執行SQLDDL
語句或須要使用真正的動態SQL,可以使用於執行建立數據庫,建立表的SQL語句等等。int executeUpdate (String SQL):
返回受SQL語句執行影響的行數。使用此方法執行預期會影響多行的SQL語句,例如:INSERT
,UPDATE
或DELETE
語句。ResultSet executeQuery(String SQL):
返回一個ResultSet
對象。 當您但願得到結果集時,請使用此方法,就像使用SELECT
語句同樣。 PreparedStatement
接口擴展了Statement
接口,它添加了比Statement
對象更好一些優勢的功能。
此語句能夠動態地提供/接受參數。
PreparedStatement pstmt = null; try { String SQL = "Update Employees SET age = ? WHERE id = ?"; pstmt = conn.prepareStatement(SQL); . . . } catch (SQLException e) { . . . } finally { pstmt.close(); }
JDBC中的全部參數都由 ?
符號做爲佔位符,這被稱爲參數標記。 在執行SQL語句以前,必須爲每一個參數(佔位符)提供值。
setXXX()
方法將值綁定到參數,其中XXX
表示要綁定到輸入參數的值的Java數據類型。 若是忘記提供綁定值,則將會拋出一個SQLException
。
每一個參數標記是它其順序位置引用。第一個標記表示位置1
,下一個位置2
等等。 該方法與Java數組索引不一樣(它不從0
開始)。
全部Statement
對象與數據庫交互的方法(a)execute()
,(b)executeQuery()
和(c)executeUpdate()
也能夠用於PreparedStatement
對象。 可是,這些方法被修改成可使用輸入參數的SQL語句。
相似Connection
對象建立Statement
和PreparedStatement
對象同樣,它還可使用一樣的方式建立CallableStatement
對象,該對象將用於執行對數據庫存儲過程的調用。
假設須要執行如下Oracle存儲過程 -
CREATE OR REPLACE PROCEDURE getEmpName (EMP_ID IN NUMBER, EMP_FIRST OUT VARCHAR) AS BEGIN SELECT first INTO EMP_FIRST FROM Employees WHERE ID = EMP_ID; END;
存在三種類型的參數:IN
,OUT
和INOUT
。 PreparedStatement
對象只使用IN
參數。CallableStatement
對象可使用上面三個參數類型。
如下是上面三種類型參數的定義 -
參數 | 描述 |
---|---|
IN | 建立SQL語句時其參數值是未知的。 使用setXXX() 方法將值綁定到IN 參數。 |
OUT | 由SQL語句返回的參數值。可使用getXXX() 方法從OUT參數中檢索值。 |
INOUT | 提供輸入和輸出值的參數。使用setXXX() 方法綁定變量並使用getXXX() 方法檢索值。 |
如下代碼片斷顯示瞭如何使用Connection.prepareCall()
方法根據上述存儲過程來實例化一個CallableStatement
對象 -
CallableStatement cstmt = null; try { //STEP 2: Register JDBC driver Class.forName("com.mysql.jdbc.Driver"); //STEP 3: Open a connection System.out.println("Connecting to database..."); conn = DriverManager.getConnection(DB_URL,USER,PASS); //STEP 4: Execute a query System.out.println("Creating statement..."); String sql = "{call getEmpName (?, ?)}"; stmt = conn.prepareCall(sql); //Bind IN parameter first, then bind OUT parameter int empID = 102; stmt.setInt(1, empID); // This would set ID as 102 // Because second parameter is OUT so register it stmt.registerOutParameter(2, java.sql.Types.VARCHAR); //Use execute method to run stored procedure. System.out.println("Executing stored procedure..." ); stmt.execute(); //Retrieve employee name with getXXX method String empName = stmt.getString(2); System.out.println("Emp Name with ID:" + empID + " is " + empName); stmt.close(); conn.close(); } catch (SQLException e) { . . . } finally { . . . }
String變量strSQL
表示存儲過程,帶有兩個參數佔位符。
使用CallableStatement
對象就像使用PreparedStatement
對象同樣。 在執行語句以前,必須將值綁定到全部參數,不然將拋出一個SQLException
異常。
若是有IN
參數,只需遵循適用於PreparedStatement
對象的相同規則和技術; 使用與綁定的Java數據類型相對應的setXXX()
方法。
使用OUT
和INOUT
參數時,必須使用一個額外的CallableStatement
對象方法registerOutParameter()
。 registerOutParameter()
方法將JDBC數據類型綁定到存儲過程並返回預期數據類型。
當調用存儲過程,可使用適當的getXXX()
方法從OUT
參數中檢索該值。 此方法將檢索到的SQL類型的值轉換爲對應的Java數據類型。
SQL語句執行後從數據庫查詢讀取數據,返回的數據放在結果集中。 SELECT
語句用於從數據庫中選擇行並在結果集中查看它們的標準方法。 java.sql.ResultSet
接口表示數據庫查詢的結果集。ResultSet
對象維護指向結果集中當前行的遊標。 術語「結果集」是指包含在ResultSet
對象中的行和列數據。
ResultSet
接口的方法能夠分爲三類:
光標能夠基於ResultSet
的屬性移動。當建立生成ResultSet
的相應Statement
時,將指定這些屬性。
JDBC提供如下鏈接方法來建立具備所需ResultSet
的語句 -
createStatement(int RSType, int RSConcurrency);
prepareStatement(String SQL, int RSType, int RSConcurrency);
prepareCall(String sql, int RSType, int RSConcurrency);
可能的RSType
值以下。若是不指定任何ResultSet
類型,將自動分配一個TYPE_FORWARD_ONLY
值。
類型 | 描述 |
---|---|
ResultSet.TYPE_FORWARD_ONLY |
光標只能在結果集中向前移動。 |
ResultSet.TYPE_SCROLL_INSENSITIVE |
光標能夠向前和向後滾動,結果集對建立結果集後發生的數據庫所作的更改不敏感。 |
ResultSet.TYPE_SCROLL_SENSITIVE |
光標能夠向前和向後滾動,結果集對建立結果集以後發生的其餘數據庫的更改敏感。 |
可能的RSConcurrency
以下。 若是不指定任何併發類型,將自動得到一個CONCUR_READ_ONLY
值。
併發 | 描述 |
---|---|
ResultSet.CONCUR_READ_ONLY |
建立只讀結果集,這是默認值。 |
ResultSet.CONCUR_UPDATABLE |
建立可更新的結果集 |
try { Statement stmt = conn.createStatement( ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); } catch(Exception ex) { .... } finally { .... }
ResultSet
接口中有幾種涉及移動光標的方法,包括 -
編號 | 方法 | 描述 |
---|---|---|
1 | public void beforeFirst() throws SQLException |
將光標移動到第一行以前 |
2 | public void afterLast() throws SQLException |
將光標移動到最後一行以後。 |
3 | public boolean first() throws SQLException |
將光標移動到第一行。 |
4 | public void last() throws SQLException |
將光標移動到最後一行。 |
5 | public boolean absolute(int row) throws SQLException |
將光標移動到指定的行。 |
6 | public boolean relative(int row) throws SQLException |
從當前指向的位置,將光標向前或向後移動給定行數。 |
7 | public boolean previous() throws SQLException |
將光標移動到上一行。 若是上一行關閉結果集,此方法返回false 。 |
8 | public boolean next() throws SQLException |
將光標移動到下一行。 若是結果集中沒有更多行,則此方法返回false 。 |
9 | public int getRow() throws SQLException |
返回光標指向的行號。 |
10 | public void moveToInsertRow() throws SQLException |
將光標移動到結果集中的特殊行,該行可用於將新行插入數據庫。當前光標位置被記住。 |
11 | public void moveToCurrentRow() throws SQLException |
若是光標當前位於插入行,則將光標移回當前行; 不然,此方法什麼也不作 |
ResultSet
接口包含數十種獲取當前行數據的方法。
每一個可能的數據類型都有一個get
方法,每一個get
方法有兩個版本 -
例如,若是對查看感興趣的列包含一個int
,則須要使用ResultSet
的其中一個getInt()
方法 -
序號 | 方法 | 描述 |
---|---|---|
1 | public int getInt(String columnName) throws SQLException |
返回名爲columnName 的列中當前行中的int 值。 |
2 | public int getInt(int columnIndex) throws SQLException |
返回指定列索引當前行中的int 值。列索引從1 開始,意味着行的第一列爲1 ,行的第二列爲2 ,依此類推。 |
ResultSet
接口包含用於更新結果集的數據的更新方法的集合。
與get方法同樣,每種數據類型都有兩種更新方法 -
例如,要更新結果集當前行的String
列,可使用如下updateString()
方法之一:
序號 | 方法 | 描述 |
---|---|---|
1 | public void updateString(int columnIndex, String s) throws SQLException |
將指定列中的String 值更改成指定的s 值。 |
2 | public void updateString(String columnName, String s) throws SQLException |
與前前的方法相似,除了使用列的名稱而不是列的索引指定。 |
有八種基本數據類型的更新方法,以及java.sql
包中的String
,Object
,URL
和SQL數據類型。
更新結果集中的一行會更改ResultSet
對象中當前行的列,但不會更改底層數據庫中的列的值。 要更新數據庫中的行,須要調用如下方法之一。
序號 | 方法 | 描述 |
---|---|---|
1 | public void updateRow() |
更新數據庫中當前行 |
2 | public void deleteRow() |
從數據庫中刪除當前行 |
3 | public void refreshRow() |
刷新結果集中的數據以反映數據庫中最近的任何更改。 |
4 | public void cancelRowUpdates() |
取消對當前行所作的任何更新。 |
5 | public void insertRow() |
在數據庫中插入一行。 只有當光標指向插入行時,才能調用此方法。 |
若是JDBC鏈接處於自動提交模式,默認狀況下,則每一個SQL語句在完成後都會提交到數據庫。
對於簡單的應用程序可能沒有問題,可是有三個緣由須要考慮是否關閉自動提交併管理本身的事務 -
事務可以控制什麼時候更改提交併應用於數據庫。 它將單個SQL語句或一組SQL語句視爲一個邏輯單元,若是任何語句失敗,整個事務將失敗。
要啓用手動事務支持,而不是使用JDBC驅動程序默認使用的自動提交模式,請調用Connection
對象的setAutoCommit()
方法。 若是將布爾的false
傳遞給setAutoCommit()
,則關閉自動提交。 也能夠傳遞一個布爾值true
來從新打開它。
例如,若是有一個名爲conn
的Connection
對象,請將如下代碼關閉自動提交 -
conn.setAutoCommit(false);
完成更改後,若要提交更改,那麼可在鏈接對象上調用commit()
方法,以下所示:
conn.commit( );
不然,要使用鏈接名爲conn
的數據庫回滾更新,請使用如下代碼 -
conn.rollback( );
如下示例說明了如何使用提交和回滾對象 -
try{ //Assume a valid connection object conn conn.setAutoCommit(false); Statement stmt = conn.createStatement(); String SQL = "INSERT INTO Employees " + "VALUES (106, 20, 'Rita', 'Tez')"; stmt.executeUpdate(SQL); //Submit a malformed SQL statement that breaks String SQL = "INSERTED IN Employees " + "VALUES (107, 22, 'Sita', 'Singh')"; stmt.executeUpdate(SQL); // If there is no error. conn.commit(); }catch(SQLException se){ // If there is any error. conn.rollback(); }
在這種狀況下,上述INSERT
語句不會成功執行,由於全部操做都被回滾了。
新的JDBC 3.0新添加了Savepoint
接口提供了額外的事務控制能力。大多數現代DBMS支持其環境中的保存點,如Oracle的PL/SQL。
設置保存點(Savepoint
)時,能夠在事務中定義邏輯回滾點。 若是經過保存點(Savepoint
)發生錯誤時,則可使用回滾方法來撤消全部更改或僅保存保存點以後所作的更改。
Connection
對象有兩種新的方法可用來管理保存點 -
Savepoint
對象。Savepoint
對象做爲參數。 該對象一般是由setSavepoint()
方法生成的保存點。有一個rollback (String savepointName)方法,它將使用事務回滾到指定的保存點。
如下示例說明了使用Savepoint
對象 -
try{ //Assume a valid connection object conn conn.setAutoCommit(false); Statement stmt = conn.createStatement(); //set a Savepoint Savepoint savepoint1 = conn.setSavepoint("Savepoint1"); String SQL = "INSERT INTO Employees " + "VALUES (106, 24, 'Curry', 'Stephen')"; stmt.executeUpdate(SQL); //Submit a malformed SQL statement that breaks String SQL = "INSERTED IN Employees " + "VALUES (107, 32, 'Kobe', 'Bryant')"; stmt.executeUpdate(SQL); // If there is no error, commit the changes. conn.commit(); }catch(SQLException se){ // If there is any error. conn.rollback(savepoint1); }
在這種狀況下,上述INSERT
語句都不會成功,由於全部操做都被回滾了。
批量處理容許將相關的SQL語句分組到批處理中,並經過對數據庫的一次調用來提交它們,一次執行完成與數據庫之間的交互。
一次向數據庫發送多個SQL語句時,能夠減小通訊開銷,從而提升性能。
DatabaseMetaData.supportsBatchUpdates()
方法來肯定目標數據庫是否支持批量更新處理。若是JDBC驅動程序支持此功能,該方法將返回true
。Statement
,PreparedStatement
和CallableStatement
的addBatch()
方法用於將單個語句添加到批處理。 executeBatch()
用於執行組成批量的全部語句。executeBatch()
返回一個整數數組,數組的每一個元素表示相應更新語句的更新計數。clearBatch()
方法刪除它們。此方法將刪除全部使用addBatch()
方法添加的語句。 可是,沒法指定選擇某個要刪除的語句。如下是使用Statement
對象的批處理的典型步驟序列 -
createStatement()
方法建立Statement
對象。setAutoCommit()
將自動提交設置爲false
。addBatch()
方法在建立的Statement
對象上添加SQL語句到批處理中。Statement
對象上使用executeBatch()
方法執行全部SQL語句。commit()
方法提交全部更改。如下是使用PrepareStatement
對象進行批處理的典型步驟順序 -
prepareStatement()
方法建立PrepareStatement
對象。setAutoCommit()
將自動提交設置爲false
。addBatch()
方法在建立的Statement
對象上添加SQL語句到批處理中。Statement
對象上使用executeBatch()
方法執行全部SQL語句。commit()
方法提交全部更改。