【搞定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分為兩部分
- JDBC 應用程式開發者介面
- 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
- atomicity 原子性:表示一個事務內的所有操作是一個整體,要麼全成功,要麼全失敗。
- consistency 一致性:表示一個事務內有一個操作失敗時,所有的更改過的資料都必須回滾到修改前的狀態。
- isolation 隔離性:事務檢視資料時資料所處的狀態,要麼是另一個併發事務修改它之前的狀態,要麼是另一事物修改它之後的狀態,事務不會檢視中間狀態的資料。
- durability 永續性:永續性事務完成之後,它對於系統的影響是永久性的。
6.3 事務的隔離級別
- 讀取未提交資料:READ UNCOMMITED
- 讀取已提交資料:READ COMMITED
- 可重複讀:REPETABLE READ
- 序列化: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的區別是什麼?
- Statement的execute(String query)方法用來執行任意的SQL查詢,如果查詢的結果是一個ResultSet,這個方法就返回true。如果結果不是ResultSet,比如insert或者update查詢,它就會返回false。我們可以通過它的getResultSet方法來獲取ResultSet,或者通過getUpdateCount()方法來獲取更新的記錄條數。
- Statement的executeQuery(String query)介面用來執行select查詢,並且返回ResultSet。即使查詢不到記錄返回的ResultSet也不會為null。我們通常使用executeQuery來執行查詢語句,這樣的話如果傳進來的是insert或者update語句的話,它會丟擲錯誤資訊為 “executeQuery method can not be used for update”的java.util.SQLException。
- 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