自從數據須要被持久化存儲,程序與數據庫之間的交互就是不可避免的操做。而早期程序與不一樣的數據庫的交互方式不一樣,意味着程序開發不得不面對數據庫的具體實現,一旦切換數據庫,又是新的學習過程,這樣開發人員的效率始終被和數據庫的交互所限制。html
根據軟件設計中的依賴倒置原則,要針對接口編程,不要針對實現編程,於是因爲數據庫的變化而引發實現的變化是違背軟件設計的基本原則的。優化的方式是在數據庫上提供一層抽象接口,不一樣數據庫根據接口完成本身的實現,而用戶使用時,只需面向接口編程,而不用關心內部的實現細節。若是須要更換數據庫,只須要要指定不一樣的數據庫標識便可。java
Java的JDBC(Java DataBase Connectivity)就是一組這樣的抽象API,經過執行SQL語句,爲多種關係型數據庫提供統一訪問。下面以mysql的一個簡單查詢爲例,介紹JDBC的接口和基本構成。mysql
try { // 裝載驅動 Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } // 數據庫鏈接 Connection conn = null; // sql執行對象 Statement statement = null; // 結果集 ResultSet resultSet = null; try { // 獲取mysql數據庫鏈接 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/lichao", "root", "root"); // 建立sql執行對象 statement = conn.createStatement(); // 執行sql,返回結果集 resultSet = statement.executeQuery("select * from user"); // 遍歷結果集,獲取數據 while(resultSet.next()){ Long id = resultSet.getLong("id"); String name = resultSet.getString("name"); Integer age = resultSet.getInt("age"); System.out.println("User[id=" + id + ",name=" + name + ",age=" + age + "]"); } } catch (SQLException e) { e.printStackTrace(); } finally { // 關閉各類資源 if(resultSet!=null) try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } if(statement!=null) try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } if(conn!=null) try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } }
connection,官方文檔的定義是程序員
A connection (session) with a specific database. SQL statements are executed and results are returned within the context of a connection.sql
鏈接即一個特定數據庫的會話,在一個鏈接的上下文中,sql語句被執行,而後結果被返回。數據庫
經過getMetaData方法能夠獲取數據庫中描述的表的信息,支持的sql語法,存儲過程以及這個鏈接的其餘性能和能力。apache
setSchema和setCatalog指定數據庫具體的catalog或schema,setReadOnly設置鏈接是否爲可讀模式,setTypeMap指定字段類型和java類的映射關係(若是爲空集合,會覆蓋原有映射map),setNetworkTimeout則是等待數據庫返回的超時時間,setHoldability改變ResultSet在commit以後的Cursor是否關閉。編程
一個Connection對象中,包含的信息很是之多。在平常開發中,最常接觸到的仍是事務相關的操做。api
事務(Transaction),解決的是多個操做執行時,同時成功或同時失敗的問題。好比銀行轉帳時,扣減A的資金和增長B的資金必須一塊兒發生,否則銀行就幹不下去了。session
可是事務的存在,在多用戶同時訪問相同的數據時會產生衝突。可能出現如下幾種不肯定狀況:
更新丟失
兩個事務同時更新一行數據(事務都未提交),一個事務對數據的更新把另外一個事務對數據的更新覆蓋了。
髒讀
一個事務讀到了另外一個事務未提交的數據操做結果(未提交的數據可能會被回滾)
不可重複讀
一個事務對同一行數據重複讀取兩次,可是卻獲得了不一樣的結果。包括兩種狀況:
爲了解決上述狀況,標準sql規範中,定義了4個事務隔離級別,不一樣的隔離級別對事務的處理不一樣。
隔離級別越高,越能保證數據的完整性和一致性,可是對併發性能的影響也越大。
JDBC中,一個connection被建立時,默認是auto-commit模式,也即一個sql statement做爲一個事務,執行完成後自動commit。若是支持多個statement組成一個事務,則要禁止auto-commit模式。
con.setAutoCommit(false);
Connection支持了標準的四種隔離級別,若是沒有設置,則查詢數據庫的默認隔離級別,也能夠用setTransactionIsolation方法手動設置。
conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
Connection使用commit和rollback方法支持事務的提交和回滾。
try { conn.setAutoCommit(false); // -- do query and update operation conn.commit(); } catch (Exception e) { conn.rollback(); }
同時使用setSavepoint和releaseSavepoint方法支持保存點的設置和釋放。
try { conn.setAutoCommit(false); // -- update 1 Savepoint s1 = conn.setSavepoint(); try { // -- update2 } catch (Exception e) { conn.releaseSavepoint(s1); } conn.commit(); } catch (Exception e) { conn.rollback(); }
DataSource表示一種建立Connection的工廠,在jdk 1.4引入,相對DriverManager的方式更優先推薦使用DataSource。支持三種實現類型:
基於DataSource產生了兩個很是經常使用的數據庫鏈接池框架:DBCP和C3P0,解決了數據庫鏈接的複用問題,極大地提升了數據庫鏈接的使用性能。看一個DBCP的簡單用例:
BasicDataSource basicDataSource = new BasicDataSource(); basicDataSource.setDriverClassName("com.mysql.jdbc.Driver"); basicDataSource.setUrl("jdbc:mysql://localhost:3306/lichao"); basicDataSource.setUsername("root"); basicDataSource.setPassword("root"); // 初始化鏈接數 basicDataSource.setInitialSize(5); // 最大鏈接數 basicDataSource.setMaxActive(30); // 最大空閒鏈接數 basicDataSource.setMaxIdle(5); // 最小空閒鏈接數 basicDataSource.setMinIdle(2); Connection conn = basicDataSource.getConnection(); // sql operation conn.close();
關於DBCP和C3P0,具體的這裏就不深刻了。
JDBC解決了面向不一樣數據庫的統一接口問題,咱們只要遵照它的接口編程,則不用關心底層數據庫的內部實現。然而程序員對編程便捷的追求是永不止步的。咱們不能接受一次簡單的查詢須要如此多的步驟,打開鏈接,建立statement對象,解析結果集,最後關閉資源,甚至連JDBC我都不想關心。個人需求很簡單,就是提出一個對數據庫的請求(請求或更新),返回給我結果,若是是查詢,直接映射到java對象最好。咱們但願更多地關注真正關心的東西(好比業務),而不是數據的傳輸過程。因而就出現了各類對JDBC的封裝框架,好比apache的dbutils。
參考文檔: