JDBC詳細總結

轉載請註明出處:http://blog.csdn.net/github_39430101/article/details/77844465

JDBC原理

JDBC是什麼

Java Database Connectivity: Java訪問數據庫的解決方案。
JDBC是Java應用程序訪問數據庫的里程碑式解決方案。Java研發者希望用相同的方式訪問不同的數據庫,以實現與具體數據庫無關的Java操作界面。
JDBC定義了一套標準接口,即訪問數據庫的通用API,不同的數據庫廠商根據各自數據庫的特點去實現這些接口。
這裏寫圖片描述

JDBC定義了一些接口

  • 驅動管理 DriverManager
  • 連接接口 Connection DatabasemetaData
  • 語句對象接口 Statement PreparedStatement CallableStatement
  • 結果集接口 ResultSet ResultMetaData

JDBC工作原理

JDBC只定義接口,具體實現由各個數據廠商負責。程序使用時只需要調用接口,實際調用的是底層數據庫廠商的實現部分。
這裏寫圖片描述

JDBC訪問數據庫的工作過程:

  1. 加載驅動,建立連接
  2. 創建語句對象
  3. 執行SQL語句
  4. 處理結果集
  5. 關閉連接

Driver接口及驅動類加載

要使用JDBC接口,需要先將對應數據庫的實現部分(驅動)加載進來
mysql:

Class.forName("com.mysql.jdbc.Driver");

這條語句的含義是:裝載驅動類,驅動類通過static塊實現在DriverManager中的自動註冊

Connection接口

Connection接口負責應用程序對數據庫的連接,在加載驅動之後,使用url、username、password三個參數,創建到具體數據庫的連接。

Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("url","username","password");

Statement接口

Statement接口用來處理髮送到數據庫的SQL語句對象,通過Connection對象創建。主要有三個常用方法:

Statement stmt = conn.createStatement();
//execute方法,如果執行的sql是查詢語句且有結果集則返回true,如果是非查詢語句或者沒有結果集,返回false
boolean flag = stmt.execute(sql);
//執行查詢語句,返回結果集
ResultSet rs = stmt.executeQuery(sql);
//執行DML語句,返回影響的記錄數
int flag = stmt.executeUpdate(sql);

ResultSet接口

執行查詢SQL語句後返回的結果集,由ResultSet接口接收。
常用處理方式:遍歷/判斷是否由結果

String sql = "select * from table";
ResultSet rs = stmt.executeQuery(sql);
while(rs.next()){
    System.out.println(rs.getInt("age")+rs.getString("name"));
}

查詢的結果存放在ResultSet對象的一系列行中,指針的最初位置在行首,使用next()方法用來在行間移動,getXXX()方法用來取得字段的內容。

JDBC基礎編程

連接管理

通過連接工具類獲取連接
在工程中,通常編寫一個訪問數據庫的工具類,此後所有訪問數據庫的操作,都從工具類中獲取連接。
實現工具類的兩種方式:

  • 直接把數據配置寫在工具類
  • 把數據庫配置寫在一個properties屬性文件裏,工具類讀入屬性文件,逐行獲取數據庫參數。

通過屬性文件維護連接屬性:

#驅動類名
jdbc.driver = com.mysql.jdbc.Driver
#連接字符串
jdbc.url = jdbc:mysql://localhost/test
jdbc.user = wz
jdbc.password = 12345

完整示例

public class Demo {
    static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
    static final String BD_URL = "jdbc:mysql://localhost/test";
    static final String USER = "username";
    static final String PASS = "password";
    public static void main(String[] args){
        Connection conn = null;
        Statement stmt = null;
        try{
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection(DB_URL,USER,PASS);
            stmt = conn.createStatement();
            String sql;
            sql = "select id,age from table";
            ResultSet rs = stmt.executeQuery(sql);
            while(rs.next()){
                int id = rs.getInt("id");
                int age = rs.getInt("age");
                System.out.print("ID"+id);
                System.out.print("Age"+age);
            }
            rs.close();
            stmt.close();
            conn.close();
        } catch(SQLException e){
                e.printStackTrace();
        } catch(SQLException e2){
                e2.printStackTrace();
        } finally {
            try{
                if(stmt != null)
                stmt.close();
            } catch (SQLException e3){
            }
            try{
                if(conn != null)
                conn.close();
            } catch(SQLException e4){
                e4.printStackTrace();
            }
        }
    }
}

連接池技術

爲什麼要使用連接池

數據庫連接的建立及關閉資源消耗巨大。傳統數據庫訪問方式:一次數據庫訪問對應一個物理連接,每次操作數據庫都要打開、關閉該物理連接,系統性能嚴重損壞。這纔出現了數據庫連接池。
系統初始運行時,主動建立足夠的連接,組成一個池,每次應用程序請求數據庫連接時,無需重新打開連接,而是從池中取出已有的連接,使用完後,不再關閉,而是歸還。

連接池中連接的釋放與使用原則

  • 應用啓動時,創建初始化數目的連接
  • 當申請時無連接可用或者達到指定的最小連接數,按增量參數值創建新的連接。
  • 爲確保連接池中最小的連接數的策略:
    1. 動態檢查:定時檢查連接池,一旦發現數量小於最小連接數,則補充相應的新連接,保證連接池正常運轉
    2. 靜態檢查:空閒連接不足時,系統才檢查是否達到最小連接數
      這裏寫圖片描述

通過DataSource獲取連接

先通過屬性文件獲取連接池參數,然後加載這些參數,獲得連接:

private static BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(driveClassName);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
Connection conn = dataSource.getConnection();

連接池參數

  • 初始連接數
  • 最大連接數
  • 最小連接數
  • 每次增加的連接數
  • 超時時間
  • 最大空閒連接
  • 最小空閒連接

JDBC核心API

Statement

通過Connection對象創建Statement的方式

Connection.createStatement();

執行INSERT,UPDATE和DELETE等DML操作

Statement.executeUpdate();

執行SELECT

Statement.executeQuery();

通過Statement對象返回SQL語句執行後的結果集:

String sql = "select id,age from table";
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);

PreparedStatement原理

Statement主要用於執行靜態SQL語句,即內容固定不變的SQL語句。Statement每執行一次都要對傳入的SQL語句編譯一次,效率較差。
某些情況下,SQL語句只是其中的參數有所不同,其餘子句完全相同,適用於PreparedStatement。
PreparedStatement的另外一個好處就是預防sql注入攻擊。
PreparedStatement是接口,繼承自Statement接口。
使用PreparedStatement時,SQL語句已提前編譯,三種常用方法 execute、executeQuery和executeUpdate已被更改,以使之不再需要參數。
這裏寫圖片描述

PreparedStatement實例包含已事先編譯的 SQL 語句,SQL 語句可有一個或多個 IN 參數,IN參數的值在 SQL 語句創建時未被指定。該語句爲每個 IN 參數保留一個問號(“?”)作爲佔位符。
每個問號的值必須在該語句執行之前,通過適當的setInt或者setString等方法提供。
由於PreparedStatement對象已預編譯過,所以其執行速度要快於 Statement 對象。因此,多次執行的 SQL 語句經常創建爲PreparedStatement對象,以提高效率。
通常批量處理時使用PreparedStatement。

PreparedStatement pstmt = conn.prepareStatement("update table set name=? where id = ?");
pstmt.setLong(1,"小明");
pstmt.setInt(2,1001);
pstmt.executeUpdate();

SQL注入

先看這樣一段SQL

String sql = "select * from t where username ='" + name +"' and password = '" + passwd + "'";

輸入用戶名和密碼參數後,數據庫接收到的完整sql語句是這樣的:

select * from t where username = 'xiaoming' and password = '123';

如果用戶輸入的passwd參數是”or 1=1”,則數據庫收到的語句是:

select * from t where username = 'scott' and password = '' or '1'='1';

此SQL語句的where語句條件爲true。即用戶不需要輸入正確的賬號密碼,也能登錄。這種現象稱爲SQL注入。

通過PreparedStatement防止SQL Injection,對JDBC而言,SQL注入攻擊只對Statement有效,對PreparedStatement無效,因爲PreparedStatement不允許在插入參數時改變SQL語句的邏輯結構。

ResultSet

結果集遍歷

String sql = "select name, id from t";
    rs = stmt.executeQuery(sql);
    while (rs.next()) {
        name = rs.getString("name");
        id = rs.getInt("id");       
Date hiredate = rs.getDate("hiredate");
    }
rs.close();

JDBC高級編程

事物簡介

事務(Transaction):數據庫中保證交易可靠的機制。在JDBC中,事務默認是自動提交的。
這裏寫圖片描述

事物四大特性:

  • 原子性(Atomicity):事務必須是原子工作單元;對於其數據修改,要麼全都執行,要麼全都不執行
  • 一致性(Consistency):事務在完成時,必須使所有的數據都保持一致狀態
  • 隔離性(Isolation):由併發事務所作的修改必須與任何其他併發事物所作的修改隔離
  • 持久性:事務完成之後,它對於系統的影響是永久性的

JDBC事物API

  • Connection.getAutoCommit() 獲得當前事務的提交方式,默認爲true
  • Connection.setAutoCommit() 設置事務的提交屬性,參數是true:自動提交;反之。
  • Connection.commit() 提交事物
  • Connection.rollback() 回滾事務

JDBC處理事務的通常模式

  • 先將事務的自動提交關閉
  • 執行事務中的若干SQL語句
  • 事務提交:SQL失敗則回滾
  • 恢復JDBC的事務提交狀態,釋放資源。
try{
// 1.定義用於在事務中執行的SQL語句
String sql1 = "update account set amount = amount - " + amount + " where id = '" + from + "'";
String sql2 = "update account set amount = amount + " + amount + " where id = '" + to + "'";
autoCommit = con.getAutoCommit(); // 2.獲得自動提交狀態
con.setAutoCommit(false); // 3.關閉自動提交
stmt.executeUpdate(sql1); // 4.執行SQL語句
stmt.executeUpdate(sql2);
con.commit(); // 5.提交
con.setAutoCommit(autoCommit); // 6.將自動提交功能恢復到原來的狀態
//其他語句;
}catch(SQLException e){
    conn.rollback();//異常時回滾
}

分頁查詢

利用數據庫的分頁SQL語句,實現在Java程序中數據表的分頁。

String sql = "select * from (select  rownumrn, empno, ename, job,mgr, sal, from (select * from emp order by empno) ) where rn between ? and ?";

兩個佔位符分別是結果集的起點和終點,計算後,替代SQL中的佔位符:

int begin = (page - 1) * pageSize + 1;
int end = begin +pageSize - 1;

page:返回第幾頁
pageSize:每頁多少條

stmt = con.prepareStatement(sql);
stmt.setInt(1, begin);
stmt.setInt(2, end);
rs = stmt.executeQuery();

這種分頁方式,每次只向數據庫請求一頁的數據量,內存壓力小適合大數據量數據表。
另一種分頁策略介紹:基於緩存的分頁技術(也被稱作假分頁),一次性把數據全部取出來放在緩存中,根據用戶要看的頁數(page)和每頁記錄數(pageSize),計算把哪些數據輸出顯示,將可滾動結果集的指針移動到指定位置。
這種方式只訪問數據庫一次 , 第一次取數比較慢 , 以後每頁都從緩存中取 , 比較快,比較適合小數據量 , 如果數據量大 , 對內存壓力較大。

JDBC實現MySQL分頁查詢

select * from t limit begin,pageSize;

begin:從第幾條開始顯示
pageSize:每頁多少條

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章