JAVA-JDBC

JAVA-JDBC

​ 參考連接:https://www.yiibai.com/jdbc/jdbc_quick_guide.htmljava

JDBC is wtf?

​ JDBC表明Java數據庫鏈接(Java Database Connectivity),它是用於Java編程語言和數據庫之間的數據庫無關鏈接的標準Java API,換句話說:JDBC是用於在Java語言編程中與數據庫鏈接的API。mysql

JDBC庫包括一般與數據庫使用相關,以下面提到的每一個任務的API -sql

  • 鏈接到數據庫
  • 建立SQL或MySQL語句
  • 在數據庫中執行SQL或MySQL查詢
  • 查看和修改結果記錄

JDBC架構

JDBC API支持用於數據庫訪問的兩層和三層處理模型,但一般,JDBC體系結構由兩層組成:數據庫

  • JDBC API:提供應用程序到JDBC管理器鏈接。
  • JDBC驅動程序API:支持JDBC管理器到驅動程序鏈接。

JDBC API使用驅動程序管理器並指定數據庫的驅動程序來提供與異構數據庫的透明鏈接。編程

JDBC驅動程序管理器確保使用正確的驅動程序來訪問每一個數據源。 驅動程序管理器可以支持鏈接到多個異構數據庫的多個併發驅動程序。數組

如下是架構圖,它顯示了驅動程序管理器相對於JDBC驅動程序和Java應用程序的位置服務器

常見的JDBC組件

JDBC API提供如下接口和類 -架構

  • DriverManager:此類管理數據庫驅動程序列表。 使用通訊子協議未來自java應用程序的鏈接請求與適當的數據庫驅動程序進行匹配。在JDBC下識別某個子協議的第一個驅動程序將用於創建數據庫鏈接。
  • Driver:此接口處理與數據庫服務器的通訊。咱們不多會直接與Driver對象進行交互。 但會使用DriverManager對象來管理這種類型的對象。 它還提取與使用Driver對象相關的信息。
  • Connection:此接口具備用於聯繫數據庫的全部方法。 鏈接(Connection)對象表示通訊上下文,即,與數據庫的全部通訊僅經過鏈接對象。
  • Statement:使用今後接口建立的對象將SQL語句提交到數據庫。 除了執行存儲過程以外,一些派生接口還接受參數。
  • ResultSet:在使用Statement對象執行SQL查詢後,這些對象保存從數據庫檢索的數據。 它做爲一個迭代器並可移動ResultSet對象查詢的數據。
  • SQLException:此類處理數據庫應用程序中發生的任何錯誤。

demo

//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數據庫鏈接

創建JDBC鏈接所涉及的編程至關簡單。 如下是基本的四個步驟 -併發

  • 導入JDBC包:使用Java語言的import語句在Java代碼開頭位置導入所需的類。
  • 註冊JDBC驅動程序:使JVM將所需的驅動程序實現加載到內存中,從而能夠知足JDBC請求。
  • 數據庫URL配置:建立一個正確格式化的地址,指向要鏈接到的數據庫(如:MySQL,Oracle和MSSQL等等)。
  • 建立鏈接對象:最後,調用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()

JDBC Statements

接口 推薦使用
Statement 用於對數據庫進行通用訪問,在運行時使用靜態SQL語句時頗有用。 Statement接口不能接受參數。
PreparedStatement 當計劃要屢次使用SQL語句時使用。PreparedStatement接口在運行時接受輸入參數。
CallableStatement 當想要訪問數據庫存儲過程時使用。CallableStatement接口也能夠接受運行時輸入參數。

Statement對象

​ 在使用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語句,例如:INSERTUPDATEDELETE語句。
  • ResultSet executeQuery(String SQL):返回一個ResultSet對象。 當您但願得到結果集時,請使用此方法,就像使用SELECT語句同樣。

PreparedStatement對象

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語句。

CallableStatement對象

​ 相似Connection對象建立StatementPreparedStatement對象同樣,它還可使用一樣的方式建立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;

​ 存在三種類型的參數:INOUTINOUTPreparedStatement對象只使用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()方法。

​ 使用OUTINOUT參數時,必須使用一個額外的CallableStatement對象方法registerOutParameter()registerOutParameter()方法將JDBC數據類型綁定到存儲過程並返回預期數據類型。

​ 當調用存儲過程,可使用適當的getXXX()方法從OUT參數中檢索該值。 此方法將檢索到的SQL類型的值轉換爲對應的Java數據類型。

JDBC結果集

​ 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);

ResultSet類型

可能的RSType值以下。若是不指定任何ResultSet類型,將自動分配一個TYPE_FORWARD_ONLY值。

類型 描述
ResultSet.TYPE_FORWARD_ONLY 光標只能在結果集中向前移動。
ResultSet.TYPE_SCROLL_INSENSITIVE 光標能夠向前和向後滾動,結果集對建立結果集後發生的數據庫所作的更改不敏感。
ResultSet.TYPE_SCROLL_SENSITIVE 光標能夠向前和向後滾動,結果集對建立結果集以後發生的其餘數據庫的更改敏感。

ResultSet的併發性

可能的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包中的StringObjectURL和SQL數據類型。

更新結果集中的一行會更改ResultSet對象中當前行的列,但不會更改底層數據庫中的列的值。 要更新數據庫中的行,須要調用如下方法之一。

序號 方法 描述
1 public void updateRow() 更新數據庫中當前行
2 public void deleteRow() 從數據庫中刪除當前行
3 public void refreshRow() 刷新結果集中的數據以反映數據庫中最近的任何更改。
4 public void cancelRowUpdates() 取消對當前行所作的任何更新。
5 public void insertRow() 在數據庫中插入一行。 只有當光標指向插入行時,才能調用此方法。

JDBC事務

若是JDBC鏈接處於自動提交模式,默認狀況下,則每一個SQL語句在完成後都會提交到數據庫。

對於簡單的應用程序可能沒有問題,可是有三個緣由須要考慮是否關閉自動提交併管理本身的事務 -

  • 提升性能
  • 保持業務流程的完整性
  • 使用分佈式事務

事務可以控制什麼時候更改提交併應用於數據庫。 它將單個SQL語句或一組SQL語句視爲一個邏輯單元,若是任何語句失敗,整個事務將失敗。

要啓用手動事務支持,而不是使用JDBC驅動程序默認使用的自動提交模式,請調用Connection對象的setAutoCommit()方法。 若是將布爾的false傳遞給setAutoCommit(),則關閉自動提交。 也能夠傳遞一個布爾值true來從新打開它。

例如,若是有一個名爲connConnection對象,請將如下代碼關閉自動提交 -

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對象有兩種新的方法可用來管理保存點 -

  • setSavepoint(String savepointName): - 定義新的保存點,它還返回一個Savepoint對象。
  • releaseSavepoint(Savepoint savepointName): - 刪除保存點。要注意,它須要一個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語句都不會成功,由於全部操做都被回滾了。

JDBC批量處理

​ 批量處理容許將相關的SQL語句分組到批處理中,並經過對數據庫的一次調用來提交它們,一次執行完成與數據庫之間的交互。

一次向數據庫發送多個SQL語句時,能夠減小通訊開銷,從而提升性能。

  • 不須要JDBC驅動程序來支持此功能。應該使用DatabaseMetaData.supportsBatchUpdates()方法來肯定目標數據庫是否支持批量更新處理。若是JDBC驅動程序支持此功能,該方法將返回true
  • StatementPreparedStatementCallableStatementaddBatch()方法用於將單個語句添加到批處理。 executeBatch()用於執行組成批量的全部語句。
  • executeBatch()返回一個整數數組,數組的每一個元素表示相應更新語句的更新計數。
  • 就像將批處理語句添加處處理中同樣,可使用clearBatch()方法刪除它們。此方法將刪除全部使用addBatch()方法添加的語句。 可是,沒法指定選擇某個要刪除的語句。

使用Statement對象進行批處理

如下是使用Statement對象的批處理的典型步驟序列 -

  • 使用createStatement()方法建立Statement對象。
  • 使用setAutoCommit()將自動提交設置爲false
  • 使用addBatch()方法在建立的Statement對象上添加SQL語句到批處理中。
  • 在建立的Statement對象上使用executeBatch()方法執行全部SQL語句。
  • 最後,使用commit()方法提交全部更改。

使用PrepareStatemeng對象進行批處理

如下是使用PrepareStatement對象進行批處理的典型步驟順序 -

  • 使用佔位符建立SQL語句。
  • 使用prepareStatement()方法建立PrepareStatement對象。
  • 使用setAutoCommit()將自動提交設置爲false
  • 使用addBatch()方法在建立的Statement對象上添加SQL語句到批處理中。
  • 在建立的Statement對象上使用executeBatch()方法執行全部SQL語句。
  • 最後,使用commit()方法提交全部更改。
相關文章
相關標籤/搜索