JDBC主要功能及職責
官方文檔對JDBC的解釋:Java數(shù)據(jù)庫連接 (JDBC,Java Database Connectivity) 合是,是針對Java編程語言與各種數(shù)據(jù)庫了罪、SQL數(shù)據(jù)庫和其他表格數(shù)據(jù)源(如電子表格或平面文件)之間獨立于數(shù)據(jù)庫的連接的行業(yè)標準。其中聪全,JDBC API 是基于 SQL 的數(shù)據(jù)庫訪問提供調用級 API泊藕。
JDBC是Java語言中提供的訪問關系型數(shù)據(jù)庫的接口。
JDBC的對數(shù)據(jù)源的操作基本如下圖:
建立數(shù)據(jù)源連接(Connection)
-
DataSource (官方推薦方式):調用JDBC 2.0提供的DataSource的getConnection()方法后,DataSource實例會返回一個與數(shù)據(jù)源建立連接的Connection對象娃圆。
UnpooledDataSource dataSource = new UnpooledDataSource(driverClassLoader, "com.mysql.jdbc.Driver", "jdbc:mysql://127.0.0.1/test", "root", "123"); Connection connection = dataSource.getConnection();
JDBC API中只提供了DataSource接口玫锋,沒有DataSource的具體實現(xiàn)。DataSource具體的實現(xiàn)由JDBC驅動程序提供讼呢,如JDBCDataSource撩鹿,MysqlDataSource等。目前主流的數(shù)據(jù)庫連接池(例如DBCP悦屏、C3P0节沦、Druid等)也都實現(xiàn)了DataSource接口
-
DriverManager: JDBC 1.0使用DriverManager類生成一個與數(shù)據(jù)源連接的Connection對象,通過重載的getConnection()方法础爬,用來獲取Connection對象甫贯。當應用程序第一次通過URL連接數(shù)據(jù)源時,DriverManager會自動加載CLASSPATH下所有的JDBC驅動看蚜。
Connection conn = DriverManager.getConnection("jdbc:hsqldb:mem:association_nested", "SA", "");
DriverManager類會嘗試加載“jdbc.drivers”系統(tǒng)屬性中引用的驅動程序類叫搁。
執(zhí)行sql語句
數(shù)據(jù)源Connection建立后,通過Connection接口提供的方法來創(chuàng)建Statement供炎、PreparedStatement或者CallableStatement對象渴逻,就可以對數(shù)據(jù)源進行查詢和修改操作。
Connection realConn = conn.getRealConnection();
Statement statement = realConn.createStatement());
statement.executeQuery("select * from t");
Statement接口實際是JDBC API中提供的SQL語句的執(zhí)行器音诫,Statement接口定義了executeQuery()查詢操作惨奕、executeUpdate()更新操作、executeBatch()批量操作纽竣、execute()查詢/更新操作墓贿、getResultSet()查詢結果集、getUpdateCount()更新操作影響的行數(shù)等等執(zhí)行和返回方法蜓氨。
a. 如果數(shù)據(jù)庫返回的更新數(shù)量大于Integer.MAX_VALUE聋袋,則需要調用executeLargeXXX()方法
b.int executeXXX(String sql, int autoGeneratedKeys)等方法,第二個參數(shù)(autoGeneratedKeys)可以為int穴吹,int[]幽勒,String[]等類型,通過autoGeneratedKeys/columnIndexes/columnNames參數(shù)告訴驅動程序哪些列是自動生成的鍵可以用于檢索港令。如果SQL語句不是INSERT語句啥容,columnNames參數(shù)就會被忽略。
處理SQL執(zhí)行結果
通過Statement接口執(zhí)行了sql語句顷霹,根據(jù)返回的ResultSet獲取sql執(zhí)行后的結果咪惠,遍歷獲取最終的查詢結果。ResultSet提供相關的getString(int columnIndex)淋淀,getBoolean(int columnIndex)等各種方法遥昧,根據(jù)JDBCType等各種枚舉最終將每列查詢結果的數(shù)據(jù)類型轉換為Java對應的類型。
關閉連接
當所有Sql語句執(zhí)行完成,并獲取結果集后炭臭,通過對應的close()方法永脓,正常關閉Statement和Connection。
JDBC 主要內容
Wrapper
Wrapper接口為使用JDBC的應用程序提供訪問原始類型的功能鞋仍,從而使用JDBC驅動中一些非標準的特性常摧。
Connection,DataSource威创,Statement落午,ResultSet,DatabaseMetaData等基礎接口繼承Wrapper肚豺,Wrapper包含unwrap()和isWrapperFor()兩個方法:
- unwrap()方法用于返回未經過包裝的JDBC驅動原始類型實例板甘,可以通過該實例調用JDBC驅動中提供的非標準的方法。
- isWrapperFor()方法用于判斷當前實例是否是JDBC驅動中某一類型的包裝類型详炬。
DataSource
DataSource優(yōu)點:
- 可以通過JNDI注冊數(shù)據(jù)源對象,然后在程序中用一個邏輯名稱來引用寞奸,JNDI會自動根據(jù)這個名稱找到與這個名稱綁定的DataSource對象呛谜。這樣就可以使用這個DataSource對象來建立和具體數(shù)據(jù)庫的連接。
- DataSource接口支持連接池和分布式事務上枪萄。連接池通過對連接的復用隐岛,不需要每次操作數(shù)據(jù)源時都新建一個物理連接,可以顯著地提高程序的效率瓷翻。
PooledConnection
當應用程序調用方法DataSource.getConnection時聚凹,返回一個Connection對象。但是當使用數(shù)據(jù)庫連接池時(例如Druid)齐帚,Connection對象實際上是到PooledConnection對象的句柄妒牙,是一個物理連接。
PooledConnection提供了連接池管理的句柄对妄。PooledConnection對象表示到數(shù)據(jù)源的物理連接湘今。當應用程序完成連接時,連接可以被回收而不是關閉剪菱,從而減少了需要建立的連接數(shù)摩瞎。開發(fā)人員一般不直接使用PooledConnection接口,它由管理連接池的中間層基礎設施使用孝常,即通過一個管理連接池的中間層基礎設施使用旗们。
- 建立連接
連接池管理器(通常是應用程序服務器)維護一個 PooledConnection的對象池。如果池中有可用的 PooledConnection 對象构灸,則連接池管理器返回一個Connection對象上渴,該對象是該物理連接的句柄。如果沒有PooledConnection對象可用,連接池管理器調用ConnectionPoolDataSource的(PooledConnection對象工廠)getPoolConnection方法來創(chuàng)建新的物理連接驰贷。一般對應實現(xiàn) ConnectionPoolDataSource 的 JDBC 驅動程序盛嘿,創(chuàng)建一個新的 PooledConnection 對象并返回一個句柄給它。如MysqlConnectionPoolDataSource實現(xiàn)如下:
public synchronized PooledConnection getPooledConnection() throws SQLException {
try {
Connection connection = this.getConnection();
MysqlPooledConnection mysqlPooledConnection = MysqlPooledConnection.getInstance((JdbcConnection)connection);
return mysqlPooledConnection;
} catch (CJException var4) {
throw SQLExceptionsMapping.translateException(var4);
}
}
- 關閉連接
當應用程序關閉連接時括袒,調用Connection的close方法次兆。當連接池完成時,連接池管理器會收到通知(使用ConnectionPool的addConnectionEventListener方法锹锰,將自己注冊為ConnectionEventListener對象)芥炭。連接池管理器停用PooledConnection對象的句柄并將PooledConnection對象返回到連接池,以便它可以再次使用恃慧。因此园蝠,當應用程序關閉其連接時,底層物理連接將被回收而不是被關閉痢士。 在連接池管理器調用PooledConnection的close方法之前彪薛,物理連接不會關閉。通常調用close方法來有序關閉服務器怠蹂。
調用Connection對象的commit()方法能夠關閉當前事務中創(chuàng)建的ResultSet對象善延。
- 分布式連接
- XAConnection繼承PooledConnection,為分布式事務提供支持的對象城侧。XAConnection 對象可以通過XAResource對象加入分布式事務易遣。事務管理器,通常是中間層服務器的一部分嫌佑,通過XAResource對象管理XAConnection豆茫。應用不直接使用這個接口;它由在中間層服務器中工作的事務管理器使用屋摇。
- XAConnection接口繼承了PooledConnection接口揩魂,具有所有PooledConnection的特性,我們可以調用XAConnection實例的getConnection()方法獲取java.sql.Connection對象
RowSet 和 ResultSet
- RowSet接口繼承java.sql包下的ResultSet接口炮温,提供了一組 JavaBeans 屬性肤京,允許將RowSet實例配置為連接到 JDBC 數(shù)據(jù)源并從數(shù)據(jù)源讀取一些數(shù)據(jù)(RowSet用于為數(shù)據(jù)源和應用程序在內容中建立一個映射)。一組 setter 方法(setInt茅特、setBytes忘分、setString等)提供了一種將輸入參數(shù)傳遞給行集的命令屬性的方法。此命令是行集在從關系數(shù)據(jù)庫獲取數(shù)據(jù)時使用的 SQL 查詢白修。RowSet接口支持 JavaBeans 事件妒峦,允許在行集上發(fā)生事件時通知應用程序中的其他組件,例如其值的更改兵睛。
RowSet對象可以建立一個與數(shù)據(jù)源的連接并在其整個生命周期中維持該連接肯骇,在這種情況下窥浪,該對象被稱為連接的RowSet
RowSet對象還可以建立一個與數(shù)據(jù)源的連接,從其獲取數(shù)據(jù)笛丙,然后關閉它漾脂,這種RowSet被稱為非連接RowSet。非連接Rowset可以在斷開時更改其數(shù)據(jù)胚鸯,然后將這些更改寫回底層數(shù)據(jù)源骨稿,不過它必須重新建立連接才能完成此操作。
相較于java.sql.ResultSet姜钳,RowSet的離線操作能夠有效地利用計算機內存減輕數(shù)據(jù)庫的負擔坦冠。由于數(shù)據(jù)操作都是在內存中進行,然后批量提交到數(shù)據(jù)源哥桥,因此靈活性和性能有很大的提高辙浑。RowSet默認是一個可滾動、可更新拟糕、可序列化的結果集判呕,而且它作為一個JavaBean組件,可以方便地在網絡間傳輸送滞,用于兩端的數(shù)據(jù)同步佛玄。通俗來講,RowSet就相當于數(shù)據(jù)庫表數(shù)據(jù)在應用程序內存中的映射累澡,我們所有的操作都可以直接與RowSet對象交互。RowSet與數(shù)據(jù)庫之間的數(shù)據(jù)同步般贼,開發(fā)人員不需要關心愧哟。
- ResultSet的類型、并行性和可保持性等屬性可以在調用Connection對象的createStatement()哼蛆、prepareStatement()或prepareCall()方法創(chuàng)建Statement對象時設置蕊梧,例如:
Connection connection = DriverManager.getConnection("abc");
Statement statement = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE, ResultSet.CLOSE_CURSORS_AT_COMMIT);
ResultSet類型
TYPE_FORWARD_ONLY(默認):游標只能向前移動,從第一行到最后一行
TYPE_SCROLL_INSENSITIVE:游標可以向前/向后滾動(相對于當前位置)腮介,也可以滾動到相對位置肥矢,ResultSet對象的修改不會影響對應的數(shù)據(jù)庫中的記錄
TYPE_SCROLL_SENSITIVE:游標可以向前/向后移動(相對于當前位置),也可以移動到絕對位置叠洗。ResultSet對象的修改會直接影響數(shù)據(jù)庫中的記錄
ResultSet并行性
CONCUR_READ_ONLY(默認):為ResultSet對象設置這種屬性后甘改,只能從ResulSet對象中讀取數(shù)據(jù),但是不能更新ResultSet對象中的數(shù)據(jù)灭抑。
CONCUR_UPDATABLE:該屬性表明十艾,既可以從ResulSet對象中讀取數(shù)據(jù),又能更新ResultSet中的數(shù)據(jù)腾节。
ResultSet可保持性
HOLD_CURSORS_OVER_COMMIT:當調用Connection對象的commit()方法時忘嫉,不關閉當前事務創(chuàng)建的ResultSet對象荤牍。
CLOSE_CURSORS_AT_COMMIT:當前事務創(chuàng)建的ResultSet對象在事務提交后會被關閉,這樣能夠提升系統(tǒng)性能庆冕。
ResultSet對象關閉后康吵,不會關閉由ResultSet對象創(chuàng)建的Blob、Clob访递、NClob或SQLXML對象晦嵌,除非調用這些對象的free()方法進行清除
事務
Connection接口中提供了一個setTransactionIsolation()方法,允許JDBC客戶端設置Connection對象的事務隔離級別力九。
Connection對象的autoCommit屬性決定什么時候結束事務耍铜。啟用自動提交后,會在每個SQL語句執(zhí)行完畢后自動提交事務跌前。當Connection對象創(chuàng)建時棕兼,默認情況下,事務自動提交是開啟的抵乓。Connection接口中setAutoCommit()方法伴挚,可以禁用事務自動提交。這種情況下灾炭,需要調用Connection接口的commit()方法進行顯式提交事務茎芋,或者調用rollback()方法回滾事務。禁用事務自動提交適用于需要將多個SQL語句作為一個事務提交或者事務由應用服務器管理蜈出。
“老生常談”的事務隔離級別:
- 臟讀田弥、幻讀、不可重復讀
- 臟讀铡原,讀取未提交的數(shù)據(jù)導致的偷厦。例如,A事務修改了一條數(shù)據(jù)燕刻,但是未提交修改只泼,此時A事務對數(shù)據(jù)的修改對其他事務是可見的,B事務中能夠讀取A事務未提交的修改卵洗。如果A事務回滾请唱,B事務中讀取的就是不正確的數(shù)據(jù)。
- 不可重復讀过蹂,(1)A事務中讀取一行數(shù)據(jù)十绑。(2)B事務中修改了該行數(shù)據(jù)。(3)A事務中再次讀取該行數(shù)據(jù)將得到不同的結果酷勺。
- 幻讀孽惰,(1)A事務中通過WHERE條件讀取若干行。(2)B事務中插入了符合條件的若干條數(shù)據(jù)鸥印。(3)A事務中通過相同的條件再次讀取數(shù)據(jù)時將會讀取到B事務中插入的數(shù)據(jù)勋功。
- 幾種事務隔離級別如下:
- TRANSACTION_NONE:表示驅動不支持事務坦报,不兼容JDBC規(guī)范的驅動程序。
- TRANSACTION_READ_UNCOMMITTED:允許事務讀取未提交的數(shù)據(jù)狂鞋,可能會出現(xiàn)臟讀片择、不可重復讀、幻讀等現(xiàn)象骚揍。
- TRANSACTION_READ_COMMITTED:在事務中進行的任何數(shù)據(jù)更改字管,在提交之前對其他事務是不可見的⌒挪唬可以防止臟讀嘲叔,不能解決不可重復讀和幻讀。
- TRANSACTION_REPEATABLE_READ:能夠解決臟讀和不可重復讀抽活,但是不能解決幻讀硫戈。
- TRANSACTION_SERIALIZABLE:事務串行執(zhí)行,能夠有效解決臟讀下硕、不可重復讀和幻讀題丁逝,但是并發(fā)效率較低
參考資料
JDBC 4.2規(guī)范文檔:https://download.oracle.com/otndocs/jcp/jdbc-4_2-mrel2-spec/index.html。
JDBC在Java 8中相關的功能:https://docs.oracle.com/javase/8/docs/technotes/guides/jdbc/jdbc_42.html
JTA規(guī)范文檔:http://download.oracle.com/otndocs/jcp/jta-1.1-spec-oth-JSpec/?submit=Download