1. 程式人生 > >【搞定JavaWeb】之JDBC

【搞定JavaWeb】之JDBC

Java資料庫連線,簡稱JDBC(java Database Connectivity),是一種用於執行SQL語句的java API,它由一組java編寫的類和介面組成。JDBC為工具/資料庫開發人員提供了一個標準的API,據此可以構建更高階的工具和介面,使資料的開發人員能夠用純java API編寫資料庫應用程式。

有了JDBC 向各種關係型資料庫傳送SQL語句就是一件很容易的事。是一個獨立於資料庫的管理系統,提供了通用的SQL資料庫存取操作的介面(CRUD:Create Read Update Delete),定義了一組同一標準,為訪問不同資料庫提供同一途徑。它是由sum公司開發的一套系統元件,供開發者直接呼叫。

1  程式是如何同資料庫進行溝通的?

資料庫本身就是一個獨立執行的應用程式,編寫應用程式就是利用網路通訊協議與資料庫進行命令交換,來進行命令的增刪查詢。

雙層架構:

 

  • 問題的重點在於,你的應用程式如何呼叫這組程式庫?

因為每個資料庫的通常有不同的通訊協議,用於連線不同資料庫在API上也會有所不同。【JDBC解決的問題】

JDBC 基本上就是用來解決這些問題,當應用程式需要連線資料庫就呼叫這組標準的API,而標準的API中的介面由資料庫廠商實現,通常稱為JDBC驅動程式(Driver)。

2  JDBC分為兩部分

  1. JDBC 應用程式開發者介面
    ((ApplicationDeveloper Interface);
  2. JDBC 驅動程式開發者介面 (Driver Developer Interface)),驅動程式介面是資料庫廠商要實現驅動程式時的規範,一般開發者並不用瞭解。

3、使用JDBC程式碼進行資料庫連線處理

開發應用程式過程中,如果要操作資料庫,我們是通過JDBC所提供的介面來實現設計程式的,理論上必須更換資料庫的時候,應用程式不用修改,直接更換資料庫驅動程式實現資料庫的更換。

Connection conn = DriverManager.getConnection(.....);  //  驅動
Statement st = conn.createStatement();   // 宣告
ResultSet rs = st.executeQuery("select * from User");   // executeQyery執行命令

假設上面段程式碼是連線MySQL資料庫,你會需要在Classpath中設定MySQL對應JDBC的驅動程式。具體來說,就是在Classpath 中設定一個JAR檔案此時應用程式、JDBC 與資料庫的關係如下圖所示:

連線MySQL資料庫:

連線Oracle資料庫:

總之:安裝好資料庫之後,我們的應用程式並不能直接使用資料庫,必須通過相應的資料庫驅動程式去和資料庫打交道。其實也就是資料庫廠商的JDBC介面的實現,即對Connection等介面的實現類的jar檔案。

4  常用介面

1、Driver介面

Driver介面由資料庫廠商提供,作為java開發人員,之需要使用Driver介面就可以了。在程式設計中要連線資料庫必須先安裝特定廠商的資料庫驅動程式,不同的資料庫有不同的裝載方法。

裝載MySQL驅動:Class.forName("com.mysql.jdbc.Driver");

裝載Oracle驅動:Class.forName("oracle.jabc.driver.OracleDriver");

2、DriverManager

JDBC的DriverManager是一個工廠類,我們通過它來建立資料庫連線。當JDBC的Driver類被載入進來時,它會自己註冊到DriverManager類裡面,你可以看下JDBC Driver類的原始碼來了解一下。

3、Connection 介面

Connection 與特定資料庫的連線(會話),在上下文中執行sql語句並返回結果。此介面有接觸資料庫的所有方法,連線物件便是通訊上下文,即與資料庫中所有的通訊都是通過此唯一的連線物件。

     DriverManager.getConnection(url, user, password) ;  建立url 中定義的資料庫連線
    
     連線Mysql 資料庫(其他資料庫類似):

Connection connection = DriverManager.getConnection("jdbc:mysql://host:port/database","user","password");

常用的方法:

  • createStatement(): 建立向資料庫傳送sql的statement物件。
  • prepareStatement(sql):建立相資料庫傳送預編譯sql的PrepredStatement物件。
  • prepareCall(sql) : 建立執行儲存過程的callableStatement物件。
  • setAutoCommit(boolean autoCommit) : 設定事務是否自動提交。
  • commit() :在連線上提交事務
  • rollback():在此連線上回滾事務

4、 Statement 介面

作用:用於執行靜態sql語句並返回它所生成結果的物件。

三種Statement 類:

1、Statement:由createStatement建立,用於傳送簡單的sql語句(不帶引數)

2、PreparedStatement:繼承自Statement 介面,由prepareStatement(sql)建立,用於傳送含有一個或者多個引數的sql語句。PreparedStatement物件採用預編譯,比Statement物件效率更高。並且可以防止sql注入。

3、CallableStatement: 繼承自PreparedStatement介面,由方法prepareCall()建立,用於儲存呼叫過程。

常用的Statement方法:

  • execute(String sql)                  執行語句,返回是否有結果集
  • executeQuery(String sql)   執行select語句,返回ResulySet結果集
  • executeUpdate(String sql) 執行insert/delete/update 操作,返回更新的行數。
  • addBatch(String sql)          把多條sql語句放到一個批處理中
  • executeBatch()                   想資料庫傳送一批sql語句執行

5、ResultSet介面

查詢結果集,提供了檢索不同型別欄位的方法,常用的有:

  • getString(int index)   /  getString(String columnName)  獲得在資料庫裡是varchar ,char 型別的資料物件
  • getFloat            獲得在資料庫裡是float型別的資料物件
  • getDate            獲得在資料庫裡是日期型別的資料物件
  • getBoolean      獲得在資料庫裡是布林型別的資料物件
  • getObjejct        獲得在資料庫裡是物件型別的資料物件 

ResultSet還提供了對結果集進行滾動的方法:

  • next() 移動到下一行
  • previous() 移動到前一行
  • absolute(int row) 移動到指定行
  • beforeFirst()移動到ResultSet 的最前面
  • afterLast()移動到ResultSet 的最後面
  • 使用後依次關閉物件及連線:ResultSet ->  Statement ->Connection

5  使用JDBC步驟

載入JDBC驅動程式 --> 建立資料庫連線Connection  -->  建立執行sql的語句Statement  --> 處理執行結果 ResultSet  --> 釋放資源

1. 註冊驅動(一次即可)

方式一:Class.forName("com.Mysql.jdbc.Driver");   

推薦使用該方式,不會對具體的驅動列產生依賴。

方式二: DriverManager.registerDriver(com.mysql.jdbc.Driver);

會造成DriverManager中產生兩個一樣的驅動,並會對具體的驅動類產生依賴

2.建立連線

Connection con = DriverManager.getConnection(url,uesr,password);

3. 建立執行sql語句的Statement

// 建立執行sql語句的Statement 
String id = "5";
String sql = "delete from table where id=" +  id;
Statement st = conn.createStatement();  
st.executeQuery(sql);  

說明:上訴的SQL語句存在SQL注入的危險。如果使用者傳入的id為“5 or 1=1”,那麼將刪除表中的所有記錄。

解決辦法:PreparedStatement 有效的防止sql注入

SQL語句在程式執行前已經進行了預編譯,當執行時動態地把引數傳給PreprareStatement時,即使引數裡有敏感字元如 or '1=1'也資料庫會作為一個引數一個欄位的屬性值來處理而不會作為一個SQL指令。

// PreparedStatement 有效的防止sql注入
String sql = “insert into user (name,pwd) values(?,?)”;  
PreparedStatement ps = conn.preparedStatement(sql);  
ps.setString(1, “col_value”);  // 佔位符順序從1開始
ps.setString(2, “123456”);     // 也可以使用setObject
ps.executeQuery();

4. 處理結果集ResultSet

ResultSet rs = ps.executeQuery();
while(rs.next()){
    rs.getString("col_name");
    rs.getInt(2);
...
 
}

5、釋放資源

資料庫連線(Connection)非常耗資源,儘量晚建立,儘量早的釋放。都要加try catch 以防前面關閉出錯,後面的就不執行了。先建立的後關閉,後建立的先關閉。

try {
    if (rs != null) {
        rs.close();
    }
} catch (SQLException e) {
    e.printStackTrace();
} finally {
    try {
        if (st != null) {
            st.close();
        }
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        try {
            if (conn != null) {
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

6  事務

事務主要重點是:ACID特點、隔離級別、提交、回滾。

6.1  事務的基本概念

一組要麼同時成功,要麼同時失敗的sql語句,是資料庫操作的一個基本執行單元。

事務開始於:

  • 連線到資料庫上,並執行一條DML語句(Insert/delete/update)。
  • 前一個事務結束後,又輸入了另外一條DML語句。

事務結束於:

  • 執行commit或rollback 語句。
  • 執行一條DCL語句,例如create table 語句,在次此情況下,會自動執行commit語句。
  • 執行一條DCL語句,例如grant語句,在此情況下,會自動自行commit語句。
  • 斷開與資料庫的連線。
  • 執行了一條DML語句,該語句失敗了,在此情況下,會為這個無效的DML語句執行rollback。

6.2  事務的ACID

  1. atomicity 原子性:表示一個事務內的所有操作是一個整體,要麼全成功,要麼全失敗。
  2. consistency 一致性:表示一個事務內有一個操作失敗時,所有的更改過的資料都必須回滾到修改前的狀態。
  3. isolation 隔離性:事務檢視資料時資料所處的狀態,要麼是另一個併發事務修改它之前的狀態,要麼是另一事物修改它之後的狀態,事務不會檢視中間狀態的資料。
  4. durability 永續性:永續性事務完成之後,它對於系統的影響是永久性的。

6.3  事務的隔離級別

  1. 讀取未提交資料:READ  UNCOMMITED
  2. 讀取已提交資料:READ  COMMITED
  3. 可重複讀:REPETABLE  READ
  4. 序列化:SERIALIZABLE

具體事務隔離級別的講解:https://blog.csdn.net/pcwl1206/article/details/84478127



下面MARK下JDBC常問的面試題:

1.什麼是JDBC,在什麼時候會用到它? 

  • JDBC的全稱是Java DataBase Connection,也就是Java資料庫連線,我們可以用它來操作關係型資料庫。JDBC介面及相關類在java.sql包和javax.sql包裡。我們可以用它來連線資料庫,執行SQL查詢,儲存過程,並處理返回的結果。
  • JDBC介面讓Java程式和JDBC驅動實現了鬆耦合,使得切換不同的資料庫變得更加簡單。

2.JDBC是如何實現Java程式和JDBC驅動的鬆耦合的? 

  • JDBC API使用Java的反射機制來實現Java程式和JDBC驅動的鬆耦合。隨便看一個簡單的JDBC示例,你會發現所有操作都是通過JDBC介面完成的,而驅動只有在通過Class.forName反射機制來載入的時候才會出現。
  • 我覺得這是Java核心庫裡反射機制的最佳實踐之一,它使得應用程式和驅動程式之間進行了隔離,讓遷移資料庫的工作變得更簡單。在這裡可以看到更多JDBC的使用示例。

3 .什麼是JDBC連線,在Java中如何建立一個JDBC連線?

JDBC連線是和資料庫伺服器建立的一個會話。你可以想像成是一個和資料庫的Socket連線。建立JDBC連線很簡單,只需要兩步:

  • A. 註冊並載入驅動:使用Class.forName(),驅動類就會註冊到DriverManager裡面並載入到記憶體裡。
  • B. 用DriverManager獲取連線物件:呼叫DriverManager.getConnnection()方法並傳入資料庫連線的URL,使用者名稱及密碼,就能獲取到連線物件。

4 .JDBC的DriverManager是用來做什麼的?

  • JDBC的DriverManager是一個工廠類,我們通過它來建立資料庫連線。當JDBC的Driver類被載入進來時,它會自己註冊到DriverManager類裡面,你可以看下JDBC Driver類的原始碼來了解一下。
  • 然後我們會把資料庫配置資訊傳入DriverManager.getConnection()方法,DriverManager會使用註冊到它裡面的驅動來獲取資料庫連線,並返回給呼叫的程式。

5 . 在Java程式中,如何獲取資料庫伺服器的相關資訊? 

  • 使用DatabaseMetaData可以獲取到伺服器的資訊。當和資料庫的連線成功建立了之後,可以通過呼叫getMetaData()方法來獲取資料庫的元資訊。DatabaseMetaData裡面有很多方法,通過它們可以獲取到資料庫的產品名稱,版本號,配置資訊等。 
DatabaseMetaData metaData = con.getMetaData(); 
String dbProduct = metaData.getDatabaseProductName();

6.JDBC的Statement是什麼? 

  • Statement是JDBC中用來執行資料庫SQL查詢語句的介面。通過呼叫連線物件的getStatement()方法我們可以生成一個Statement物件。我們可以通過呼叫它的execute(),executeQuery(),executeUpdate()方法來執行靜態SQL查詢。
  • 預設情況下,一個Statement同時只能開啟一個ResultSet。如果想操作多個ResultSet物件的話,需要建立多個Statement。Statement介面的所有execute方法開始執行時都預設會關閉當前開啟的ResultSet。

7 .execute,executeQuery,executeUpdate的區別是什麼?

  1. Statement的execute(String query)方法用來執行任意的SQL查詢,如果查詢的結果是一個ResultSet,這個方法就返回true。如果結果不是ResultSet,比如insert或者update查詢,它就會返回false。我們可以通過它的getResultSet方法來獲取ResultSet,或者通過getUpdateCount()方法來獲取更新的記錄條數。
  2. Statement的executeQuery(String query)介面用來執行select查詢,並且返回ResultSet。即使查詢不到記錄返回的ResultSet也不會為null。我們通常使用executeQuery來執行查詢語句,這樣的話如果傳進來的是insert或者update語句的話,它會丟擲錯誤資訊為 “executeQuery method can not be used for update”的java.util.SQLException。
  3. Statement的executeUpdate(String query)方法用來執行insert或者update/delete(DML)語句,或者什麼也不返回的DDL語句。返回值是int型別,如果是DML語句的話,它就是更新的條數;如果是DDL的話,就返回0。
  • 只有當你不確定是什麼語句的時候才應該使用execute()方法,否則應該使用executeQuery或者executeUpdate方法。

8 .JDBC的PreparedStatement是什麼?

  • PreparedStatement物件代表的是一個預編譯的SQL語句。用它提供的setter方法可以傳入查詢的變數。
  • 由於PreparedStatement是預編譯的,通過它可以將對應的SQL語句高效的執行多次。由於PreparedStatement自動對特殊字元轉義,避免了SQL注入攻擊,因此應當儘量的使用它。

9 . PreparedStatement中如何注入NULL值? 

  • 可以使用它的setNull方法來把null值繫結到指定的變數上。setNull方法需要傳入引數的索引以及SQL欄位的型別,像這樣: ps.setNull(10, java.sql.Types.INTEGER);

10 .相對於Statement,PreparedStatement的優點是什麼? 

  • PreparedStatement有助於防止SQL注入,因為它會自動對特殊字元轉義。
  • PreparedStatement可以用來進行動態查詢。
  • PreparedStatement執行更快。尤其當你重用它或者使用它的拼量查詢介面執行多條語句時。
  • 使用PreparedStatement的setter方法更容易寫出面向物件的程式碼,而Statement的話,我們得拼接字串來生成查詢語句。如果引數太多了,字串拼接看起來會非常醜陋並且容易出錯。

11 .如何回滾事務?

  • 通過Connection物件的rollback方法可以回滾事務。它會回滾這次事務中的所有修改操作,並釋放當前連線所持有的資料庫鎖。

12 .JDBC的DataSource是什麼,有什麼好處?

DataSource即資料來源,它是定義在javax.sql中的一個介面,跟DriverManager相比,它的功能要更強大。我們可以用它來建立資料庫連線,當然驅動的實現類會實際去完成這個工作。除了能建立連線外,它還提供瞭如下的特性:

  • 快取PreparedStatement以便更快的執行
  • 可以設定連線超時時間
  • 提供日誌記錄的功能
  • ResultSet大小的最大閾值設定
  • 通過JNDI的支援,可以為servlet容器提供連線池的功能

13.如何通過JDBC的DataSource和Apache Tomcat的JNDI來建立連線池? 

  • -------

14.什麼是資料庫的隔離級別?

  • 當我們為了資料的一致性使用事務時,資料庫系統用鎖來防止別人訪問事務中用到的資料。資料庫通過鎖來防止髒讀,不可重複讀(Non-Repeatable Reads)及幻讀(Phantom-Read)的問題。
  • 資料庫使用JDBC設定的隔離級別來決定它使用何種鎖機制,我們可以通過Connection的getTransactionIsolation和setTransactionIsolation方法來獲取和設定資料庫的隔離級別。

15.JDBC的RowSet是什麼,有哪些不同的RowSet? 

  • RowSet用於儲存查詢的資料結果,和ResultSet相比,它更具靈活性。RowSet繼承自ResultSet,因此ResultSet能幹的,它們也能,而ResultSet做不到的,它們還是可以。RowSet介面定義在javax.sql包裡。

RowSet提供的額外的特性有: 

  • 提供了Java Bean的功能,可以通過settter和getter方法來設定和獲取屬性。RowSet使用了JavaBean的事件驅動模型,它可以給註冊的元件傳送事件通知,比如遊標的移動,行的增刪改,以及RowSet內容的修改等。
  • RowSet物件預設是可滾動,可更新的,因此如果資料庫系統不支援ResultSet實現類似的功能,可以使用RowSet來實現。

16 .常見的JDBC異常有哪些? 

  • java.sql.SQLException——這是JDBC異常的基類。
  • java.sql.BatchUpdateException——當批處理操作執行失敗的時候可能會丟擲這個異常。這取決於具體的JDBC驅動的實現,它也可能直接丟擲基類異常java.sql.SQLException。
  • java.sql.SQLWarning——SQL操作出現的警告資訊。
  • java.sql.DataTruncation——欄位值由於某些非正常原因被截斷了(不是因為超過對應欄位型別的長度限制)。

轉載自:

1、https://mp.weixin.qq.com/s/pje-umK0ySzzaS_yX4qsuw

2、https://blog.csdn.net/limin0983/article/details/73500035

3、https://blog.csdn.net/qq_33290787/article/details/51924963