1. 程式人生 > 其它 >藍橋杯Java課程學習——JDBC(二)

藍橋杯Java課程學習——JDBC(二)

技術標籤:藍橋杯Java課程學習筆記資料庫mysqljavajdbc

文章目錄





JDBC操作資料庫

JDBC操作資料庫
通過使用 JDBC Statement, CallableStatementPreparedStatement介面定義的方法和屬性,使可以使用 SQL 或 PL/SQL 命令和從資料庫接收資料。它們還定義了許多方法,幫助消除 Java 和資料庫之間資料型別的差異。

介面應用場景
Statement當在執行時使用靜態 SQL 語句時(Statement 介面不能接收引數)
CallableStatement當要訪問資料庫中的儲存過程時(CallableStatement 物件的介面還可以接收執行時輸入引數)
PreparedStatement當計劃多次使用 SQL 語句時(PreparedStatement 介面接收在執行時輸入引數)

Statement

我們要使用Statement介面,第一步肯定是建立一個Statement物件了。我們需要使用Connection物件的createStatement()方法進行建立。

Statement stmt = null;
try { stmt = conn.createStatement( ); . . . } catch (SQLException e) { . . . } finally { . . . }

一旦建立了一個 Statement 物件,我們就可以用它來執行 SQL 語句了

方法說明
boolean execute(String SQL)如果 ResultSet 物件可以被檢索返回布林值 true,否則返回 false。使用這個方法來執行 SQL DDL 語句,或當需要使用真正的動態 SQL
int executeUpdate(String SQL)用於執行 INSERT、UPDATE 或 DELETE 語句以及 SQLDDL(資料定義語言)語句。返回值是一個整數,指示受影響的行數(即更新計數)
ResultSet executeQuery(String SQL)返回 ResultSet 物件。用於產生單個結果集的語句,例如 SELECT 語句

正如關閉一個 Connection 物件來釋放資料庫連線資源,出於同樣的原因,也應該關閉 Statement 物件。

Statement stmt = null;
try {
   stmt = conn.createStatement( );
   . . .
}
catch (SQLException e) {
   . . .
}
finally {
   stmt.close();
}

注:如果關閉了 Connection 物件首先它會關閉 Statement 物件,然而應該始終明確關閉 Statement 物件,以確保正確的清除。

PreparedStatement

PreparedStatement 介面擴充套件了Statement介面,有利於高效地執行多次使用的 SQL 語句。我們先來建立一個PreparedStatement物件。 Statement為一條 SQL 語句生成執行計劃。如果要執行兩條 SQL 語句,會生成兩個執行計劃。一萬個查詢就生成一萬個執行計劃!

select colume from table where colume=1;
select colume from table where colume=2;

PreparedStatement 用於使用繫結變數重用執行計劃。

select colume from table where colume=:x;

通過 set 不同資料,只需要生成一次執行計劃,並且可以重用。

PreparedStatement pstmt = null;
try {

/*
在JDBC中所有的引數都被代表?符號,這是已知的引數標記。在執行SQL語句之前,必須提供值的每一個引數。
*/
   String SQL = "Update Students SET age = ? WHERE id = ?";
   pstmt = conn.prepareStatement(SQL);
   . . .
}
/*

setXXX()方法將值繫結到引數,其中XXX表示希望繫結到輸入引數值的 Java 資料型別。如果忘了提供值,將收到一個 SQLException。
*/
catch (SQLException e) {
   . . .
}
finally {
//同理,我們需要關閉 PreparedStatement 物件
   pstmt.close();
}

示例

  1. 先建立資料庫和相應的內容:
create database EXAMPLE;
use EXAMPLE

create table Students
(
id int not null,
age int not null,
name varchar(255),
primary key(id)
);
insert into Students values(1,18,'Tom'),
(2,20,'Aby'),(4,20,'Tomson');
  1. Java程式碼
import java.sql.*;

public class JdbcTest {
   // JDBC 驅動器的名稱和資料庫地址
   static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
   static final String DB_URL = "jdbc:mysql://localhost/EXAMPLE";


   static final String USER = "root";
   static final String PASS = "";

   public static void main(String[] args) {
       Connection conn = null;
       PreparedStatement stmt = null;
       try{
           //註冊 JDBC 驅動器
           Class.forName("com.mysql.jdbc.Driver");

           //開啟連線
           System.out.println("Connecting to database...");
           conn = DriverManager.getConnection(DB_URL,USER,PASS);

           //執行查詢
           System.out.println("Creating statement...");
           //這裡我們要更改一個同學的年齡,引數待定
           String sql = "UPDATE Students set age=? WHERE id=?";
           stmt = conn.prepareStatement(sql);

           //將值繫結到引數,引數從左至右序號為1,2...
           stmt.setInt(1, 22);  // 繫結 age 的值(序號為1)
           stmt.setInt(2, 1); // 繫結 ID 的值

           // 更新 ID 為1的同學的年齡
           int rows = stmt.executeUpdate();
           System.out.println("被影響的行數 : " + rows );

           // 查詢所有記錄,並顯示.
           sql = "SELECT id, name, age FROM Students";
           ResultSet rs = stmt.executeQuery(sql);

           //處理結果集
           while(rs.next()){
               //檢索
               int id  = rs.getInt("id");
               int age = rs.getInt("age");
               String name = rs.getString("name");


               //顯示
               System.out.print("ID: " + id);
               System.out.print(", Age: " + age);
               System.out.print(", Name: " + name);
               System.out.println();
           }
           //清理
           rs.close();
           stmt.close();
           conn.close();
       }catch(SQLException se){
           se.printStackTrace();
       }catch(Exception e){
           e.printStackTrace();
       }finally{
           try{
               if(stmt!=null)
                   stmt.close();
           }catch(SQLException se2){
           }
    try{
         if(conn!=null)
                 conn.close();
      }catch(SQLException se){
              se.printStackTrace();
          }
       }
           System.out.println("Goodbye!");
   }
}

CallableStatement

CallableStatement物件為所有的 DBMS 提供了一種以標準形式呼叫儲存過程的方法。儲存過程儲存在資料庫中。對儲存過程的呼叫是 CallableStatement 物件所含的內容。三種類型的引數有:INOUTINOUT

PreparedStatement物件只使用 IN 引數。 CallableStatement物件可以使用所有三個

引數描述
IN它的值是在建立 SQL 語句時未知的引數,將 IN 引數傳給 CallableStatement 物件是通過 setXXX() 方法完成的
OUT其值由它返回的 SQL 語句提供的引數。從 OUT 引數的 getXXX() 方法檢索值
INOUT同時提供輸入和輸出值的引數,繫結的 setXXX() 方法的變數,並使用 getXXX() 方法檢索值

在 JDBC 中呼叫儲存過程的語法如下所示。注意,方括號表示其間的內容是可選項;方括號本身並不是語法的組成部份。

{call 儲存過程名[(?, ?, ...)]}

返回結果引數的過程的語法為:

{? = call 儲存過程名[(?, ?, ...)]}

不帶引數的儲存過程的語法類似:

{call 儲存過程名}

CallableStatement 物件是用 Connection 方法 prepareCall 建立的。

CallableStatement cstmt = null;
try {
   String SQL = "{call getEXAMPLEName (?, ?)}";
   cstmt = conn.prepareCall (SQL);
   . . .
}
catch (SQLException e) {
   . . .
}
finally {
   cstmt.close();
}

JDBC 結果集

結果集通常是通過執行查詢資料庫的語句生成,表示資料庫查詢結果的資料表。

ResultSet 介紹

ResultSet物件具有指向其當前資料行的游標。最初,游標被置於第一行之前。next方法將游標移動到下一行;因為該方法在ResultSet物件沒有下一行時返回false,所以可以在while 迴圈中使用它來迭代結果集。游標可以方便我們對結果集進行遍歷。預設的ResultSet物件不可更新,僅有一個向前移動的游標。因此,只能迭代它一次,並且只能按從第一行到最後一行的順序進行。

ResultSet介面的方法可分為三類:

  • 導航方法:用於移動游標
  • 獲取方法:用於檢視當前行的游標所指向的列中的資料
  • 更新方法:用於更新當前行的列中的資料

JDBC 提供下列連線方法來建立所需的ResultSet語句:

createStatement(int RSType, int RSConcurrency);

prepareStatement(String SQL, int RSType, int RSConcurrency);

prepareCall(String sql, int RSType, int RSConcurrency);

RSType 表示 ResultSet 物件的型別
RSConcurrency 是 ResultSet 常量,用於指定一個結果集是否為只讀或可更新。
ResultSet 的型別,如果不指定 ResultSet 型別,將自動獲得一個是 TYPE_FORWARD_ONLY:

型別描述
ResultSet.TYPE_FORWARD_ONLY遊標只能向前移動的結果集
ResultSet.TYPE_SCROLL_INSENSITIVE遊標可以向前和向後滾動,但不及時更新,就是如果資料庫裡的資料修改過,並不在 ResultSet 中反應出來
ResultSet.TYPE_SCROLL_SENSITIVE遊標可以向前和向後滾動,並及時跟蹤資料庫的更新,以便更改 ResultSet 中的資料

併發性的 ResultSet,如果不指定任何併發型別,將自動獲得一個為 CONCUR_READ_ONLY

併發描述
ResultSet.CONCUR_READ_ONLY建立結果集只讀。這是預設的
ResultSet.CONCUR_UPDATABLE建立一個可更新的結果集

如初始化一個 Statement 物件來建立一個雙向、可更新的 ResultSet 物件:

try {
   Statement stmt = conn.createStatement(
                           ResultSet.TYPE_SCROLL_INSENSITIVE,
                           ResultSet.CONCUR_UPDATABLE);
}
catch(Exception ex) {
   ....
}
finally {
   ....
}

導航

方法說明
public void beforeFirst() throws SQLException將游標移動到正好位於第一行之前
public void afterLast() throws SQLException將游標移動到剛剛結束的最後一行
public boolean first() throws SQLException將游標移動到第一行
public void last() throws SQLException將游標移動到最後一行
public boolean absolute(int row) throws SQLException將游標移動到指定的行
public boolean relative(int row) throws SQLException從它目前所指向向前或向後移動游標行的給定數量
public boolean previous() throws SQLException將游標移動到上一行。上一行關閉的結果集此方法返回 false
public boolean next() throws SQLException將游標移動到下一行。如果沒有更多的行結果集中的此方法返回 false
public int getRow() throws SQLException返回的行號,該游標指向的行
public void moveToInsertRow() throws SQLException將游標移動到一個特殊的行,可以用來插入新行插入到資料庫中的結果集。當前游標位置被記住
public void moveToCurrentRow() throws SQLException移動游標返回到當前行,如果游標在當前插入行,否則,這個方法不執行任何操作

示例

  1. 先建立資料庫和相應的內容:
create database EXAMPLE;
use EXAMPLE

create table Students
(
id int not null,
age int not null,
name varchar(255),
primary key(id)
);
insert into Students values(1,18,'Tom'),
(2,20,'Aby'),(4,20,'Tomson');
  1. Java程式碼
import java.sql.*;

public class JdbcTest {
   // JDBC 驅動器名稱 和資料庫地址
   static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
   //資料庫的名稱為 EXAMPLE
   static final String DB_URL = "jdbc:mysql://localhost/EXAMPLE";

   // 資料庫使用者和密碼
   static final String USER = "root";
   static final String PASS = "";

   public static void main(String[] args) {
       Connection conn = null;
       Statement stmt = null;
       try{
           //註冊JDBC 驅動程式
           Class.forName("com.mysql.jdbc.Driver");

           //開啟連線
           System.out.println("Connecting to database...");
           conn = DriverManager.getConnection(DB_URL,USER,PASS);



           System.out.println("Creating statement...");
           //建立所需的ResultSet,雙向,只讀
           stmt = conn.createStatement(
                           ResultSet.TYPE_SCROLL_INSENSITIVE,
                           ResultSet.CONCUR_READ_ONLY);
           String sql;
           sql = "SELECT id, name, age FROM Students";
           ResultSet rs = stmt.executeQuery(sql);

           // 將游標移到最後一行
           System.out.println("Moving cursor to the last...");
           rs.last();

           //處理結果集
           System.out.println("Displaying record...");
           int id  = rs.getInt("id");
           int age = rs.getInt("age");
           String name = rs.getString("name");


           //顯示
           System.out.print("ID: " + id);
           System.out.print(", Age: " + age);
           System.out.print(", Name: " + name);
           System.out.println();

           // 將游標移到第一行
           System.out.println("Moving cursor to the first row...");
           rs.first();


           System.out.println("Displaying record...");
           id  = rs.getInt("id");
           age = rs.getInt("age");
           name = rs.getString("name");


           //顯示
           System.out.print("ID: " + id);
           System.out.print(", Age: " + age);
           System.out.print(", Name: " + name);

           //將游標移至下一行
           System.out.println("Moving cursor to the next row...");
           rs.next();


           System.out.println("Displaying record...");
           id  = rs.getInt("id");
           age = rs.getInt("age");
           name = rs.getString("name");

           // 顯示
           System.out.print("ID: " + id);
           System.out.print(", Age: " + age);
           System.out.print(", Name: " + name);

           rs.close();
           stmt.close();
           conn.close();
       }catch(SQLException se){
           se.printStackTrace();
       }catch(Exception e){
           e.printStackTrace();
       }finally{
           try{
               if(stmt!=null)
                   stmt.close();
           }catch(SQLException se2){
           }
           try{
               if(conn!=null)
                   conn.close();
           }catch(SQLException se){
               se.printStackTrace();
           }
       }
       System.out.println("Goodbye!");
   }
}

獲取

ResultSet介面中我們經常使用 get方法來檢視結果集。

方法說明
public int getInt(String columnName) throws SQLException當前行中名為 ColumnName 列的值
public int getInt(int columnIndex) throws SQLException當前行中指定列的索引的值。列索引從 1 開始,意味著一個行的第一列是 1,行的第二列是 2,依此類推

當然還有getString()等等。

更新

方法說明
public void updateString(int columnIndex, String s) throws SQLException指定列中的字串更改為 s 的值
public void updateString(String columnName, String s) throws SQLException類似於前面的方法,不同之處在於由它的名稱,而不是它的索引指定的列

類似的還有updateDouble()等等。

我們在更新了結果集中的內容,當然需要更新一下資料庫了。我們可以呼叫下面的方法更新資料庫。

方法說明
public void updateRow()通過更新資料庫中相應的行更新當前行
public void deleteRow()從資料庫中刪除當前行
public void refreshRow()重新整理在結果集的資料,以反映最新變化在資料庫中
public void cancelRowUpdates()取消所做的當前行的任何更新
public void insertRow()插入一行到資料庫中。當游標指向插入行此方法只能被呼叫
Statement stmt = conn.createStatement(
                           ResultSet.TYPE_SCROLL_INSENSITIVE,
                           ResultSet.CONCUR_UPDATABLE);

String sql = "SELECT id, name, age FROM Students";
ResultSet rs = stmt.executeQuery(sql);

//結果集中插入新行
rs.moveToInsertRow();
rs.updateInt("id",5);
rs.updateString("name","John");
rs.updateInt("age",21);
//更新資料庫
rs.insertRow();

JDBC 事務

我們在編寫 java 程式的時候,在預設情況下,JDBC 連線是在自動提交模式下,即每個 SQL 語句都是在其完成時提交到資料庫。但有時候我們為了提高程式執行的效能或者保持業務流程的完整性,以及使用了分散式事務管理方式,這個時候我們可能想關閉自動提交而自己管理和控制自己的事務。
讓多條 SQL 在一個事務中執行,並且保證這些語句是在同一時間共同執行的時候,我們就應該為這多條語句定義一個事務。一個事務是把單個 SQL 語句或一組 SQL 語句作為一個邏輯單元,並且如果事務中任何語句失敗,則整個事務失敗。
如果我們要啟動一個事務,而不是讓 JDBC 驅動程式預設使用 auto-commit 模式支援。這個時候我們就要使用 Connection 物件的setAutoCommit()方法。我們傳遞一個布林值 false 到setAutoCommit()中,就可以關閉自動提交。反之我們傳入一個 true 便將其重新開啟。

Connection conn = null;
conn = DriverManager.getConnection(URL);
//關閉自動提交
conn.setAutoCommit(false);

我們關閉了自動提交後,**如果我們要提交資料庫更改怎麼辦呢?**這時候就要用到我們的提交和回滾了。我們要提交更改,可以呼叫commit()方法:

conn.commit();

尤其不要忘記,在 catch 塊內添加回滾事務,表示操作出現異常,撤銷事務:

conn.rollback();

插入資料

JDBC 插入資料使用的頻率非常高,接下來說明如何使用 JDBC 插入資料。

示例

import java.sql.*;

public class JdbcInsertTest {
    public static Connection connection = null;
    public static void main(String[] args) {
        try {
            Class.forName("com.mysql.jdbc.Driver");
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/EXAMPLE", "root", "");
            Statement statement = connection.createStatement();
            //單條插入
            boolean execute = statement.execute("insert into Students values (0,1,'shiyanlou')");
            if (execute) {
                System.out.println("插入失敗");
            }else {
                System.out.println("單條插入成功");
            }
            // 批量插入  需要關閉自動提交
            connection.setAutoCommit(false);
            String sql = "insert into Students values (?,?,?)";
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            //設定插入的值
            for (int i = 1; i <= 10; i++) {
                preparedStatement.setInt(1, i);
                preparedStatement.setInt(2, i + 10);
                preparedStatement.setString(3, "shiyanlou");
                preparedStatement.addBatch();
            }
            //執行批量插入,使用executeBatch 方法
            preparedStatement.executeBatch();
            //提交到資料庫
            connection.commit();
            System.out.println("提交批量插入完成");
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }

    }
}