深入解析Java中的JDBC事務(wù)
事務(wù)
事務(wù)是一步或多步組成操作序列組成的邏輯執(zhí)行單元,這個(gè)序列要么全部執(zhí)行,要么則全部放棄執(zhí)行。事務(wù)的四個(gè)特性:原子性(Atomicity)、一致性(Consistency)、隔離性(IsoIation)和持續(xù)性(Durability)原子性(Atomicity):事務(wù)應(yīng)用最小的執(zhí)行單元,不可再分。是事務(wù)中不可再分的最小邏輯執(zhí)行體。
一致性(Consistency):事務(wù)的執(zhí)行結(jié)果,必須使數(shù)據(jù)庫(kù)的從一個(gè)一致性的狀態(tài)變到另一個(gè)一致性的狀態(tài)。
隔離線(IsoIation):各個(gè)事務(wù)的執(zhí)行互不干擾,任意一個(gè)事務(wù)的內(nèi)部操作對(duì)其他并發(fā)的事務(wù),都是隔離的。也就是:并發(fā)執(zhí)行的事務(wù)之間不能看到對(duì)方的中間狀態(tài),并發(fā)執(zhí)行的事務(wù)之間不能互相影響。
持續(xù)性(Durability):持續(xù)性也稱為持久性(Persistence),指事務(wù)一旦提交,對(duì)數(shù)據(jù)所做的任何改變,都要記錄到永久存儲(chǔ)器中,通常就是保存在物理數(shù)據(jù)庫(kù)中。
通常數(shù)據(jù)庫(kù)的事務(wù)涉及到的語(yǔ)句有:一組DML(Data Munipulation Language,數(shù)據(jù)操作語(yǔ)言)語(yǔ)句,這組DML語(yǔ)句修改后數(shù)據(jù)將保持較好的一致性; 操作表的語(yǔ)句,如插入、修改、刪除等;一個(gè)DDL(Data Definition Language,數(shù)據(jù)定義語(yǔ)言)語(yǔ)句,操作數(shù)據(jù)對(duì)象的語(yǔ)言,有create、alter、drop。一個(gè)DCL(Data Control Language,數(shù)據(jù)控制語(yǔ)言)語(yǔ)句,主要有g(shù)rant、revoke語(yǔ)句。 DDL和DCL語(yǔ)句最多只能有一個(gè),因?yàn)樗鼈兌紩?huì)導(dǎo)致事務(wù)的立即提交。當(dāng)事務(wù)所包含的全部數(shù)據(jù)庫(kù)操作都成功執(zhí)行后,應(yīng)該提交事務(wù),使這些修改永久生效。事務(wù)提交有兩種方式:顯示提交和自動(dòng)提交。顯示提交:使用commit提交自動(dòng)提交:執(zhí)行DLL或DCL,或者程序正常退出 當(dāng)事務(wù)包含的任意一個(gè)數(shù)據(jù)庫(kù)操作執(zhí)行失敗后,應(yīng)該回滾(rollback)事務(wù),使該事務(wù)中所作的修改全部失效。事務(wù)的回滾方式有兩種:顯示回滾和自動(dòng)回滾。顯示回滾:使用rollback自動(dòng)回滾:系統(tǒng)錯(cuò)誤或強(qiáng)行退出。
事務(wù)并發(fā)處理可能的問(wèn)題
1、臟讀(dirty read):一個(gè)事務(wù)讀取了另一個(gè)事務(wù)尚未提交的數(shù)據(jù)
2、不可重復(fù)讀(non-repeatable read):一個(gè)事務(wù)的操作導(dǎo)致另一個(gè)事務(wù)前后兩次讀到不同的數(shù)據(jù)
3、幻讀(phantom read):一個(gè)事務(wù)的操作導(dǎo)致另一個(gè)事務(wù)前后兩次查詢的結(jié)果數(shù)據(jù)量不同
舉例:
事務(wù)A、B并發(fā)執(zhí)行時(shí):
- 當(dāng)A事務(wù)update后,B事務(wù)select讀取到A尚未提交的數(shù)據(jù),此時(shí)A事務(wù)rollback,則B讀到的數(shù)據(jù)是無(wú)效的臟數(shù)據(jù)
- 當(dāng)B事務(wù)select讀取數(shù)據(jù)后,A事務(wù)update操作更改B事務(wù)select到的數(shù)據(jù),此時(shí)B事務(wù)再次讀取該數(shù)據(jù),發(fā)現(xiàn)前后兩次的數(shù)據(jù)不一樣
- 當(dāng)B事務(wù)select讀取數(shù)據(jù)后,A事務(wù)insert或delete了一條滿足A事務(wù)的select條件的記錄,此時(shí)B事務(wù)再次select,發(fā)現(xiàn)查詢到前次不存在的記錄,或者前次的某個(gè)記錄不見了
Java JDBC事務(wù)機(jī)制
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class JDBCTransaction {
public static final String URL = "com.mysql.jdbc.Driver";
public static final String USER = "root";
public static final String PASSWD = "123456";
public static void jdbcTransaction(int id) {
Connection conn = null;
PreparedStatement pstmtupdate = null;
PreparedStatement pstmtquery = null;
String updatesql = "更新sql";
String querysql = "查詢sql";
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(URL, USER, PASSWD);
conn.setAutoCommit(false); // 自動(dòng)提交設(shè)置為false
// 執(zhí)行更新操作
pstmtupdate = conn.prepareStatement(updatesql);
pstmtupdate.executeUpdate();
// 執(zhí)行查找操作
pstmtquery = conn.prepareStatement(querysql);
pstmtquery.executeQuery();
conn.commit();
conn.setAutoCommit(true);
pstmtupdate.close();
pstmtquery.close();
conn.close();
} catch (Exception e) {
try {
conn.rollback();
} catch (SQLException e1) {}
e.printStackTrace();
} finally {
try {
if (pstmtupdate != null) {
pstmtupdate.close();
}
if (pstmtquery != null) {
pstmtquery.close();
}
if (conn != null) {
conn.close();
}
} catch (SQLException e2) {}
}
}
}
JDBC的事務(wù)支持
JDBC的Connection也支持事物,Connection默認(rèn)打開自動(dòng)提交,即關(guān)閉事物。也就是說(shuō),每條SQL語(yǔ)句執(zhí)行就會(huì)立即提交到數(shù)據(jù)庫(kù),永久生效,無(wú)法對(duì)其進(jìn)行操作。關(guān)閉Connection的自動(dòng)提交,開啟事物。Connection的setAutoCommit方法即可:connection.setAutoCommit(false);通過(guò)connection.getAutoCommit()來(lái)獲取事物的模式。當(dāng)我們開啟事物后,在當(dāng)前Connection中完成的數(shù)據(jù)庫(kù)操作,都不會(huì)立即提交到數(shù)據(jù)庫(kù),需要調(diào)用Connection的commit方法才行。如果有語(yǔ)句執(zhí)行失敗,可以調(diào)用rollback來(lái)回滾。注意:如果Connection遇到未處理的SQLException異常時(shí),系統(tǒng)將非正常退出,系統(tǒng)會(huì)自動(dòng)回滾該事務(wù)。如果程序捕捉了該異常,則需要在異常處理中顯示回滾事務(wù)。 Connection提供了設(shè)置事務(wù)中間保存點(diǎn)的方法:setSavepoint,有2個(gè)方法可以設(shè)置中間點(diǎn):Savepoint setSavepoint():在當(dāng)前事務(wù)中創(chuàng)建一個(gè)未命名的中間點(diǎn),并返回該中間點(diǎn)的Savepoint對(duì)象。Savepoint setSavepoint(String name):當(dāng)前事務(wù)中創(chuàng)建一個(gè)具有指定名稱的中間點(diǎn),并返回該中間點(diǎn)的Savepoint對(duì)象通常setSavepoint(String name)設(shè)置中間點(diǎn)的名稱,事務(wù)回滾并不是通過(guò)中間點(diǎn)的名稱進(jìn)行回滾的,而是根據(jù)中間點(diǎn)對(duì)象進(jìn)行回滾的。設(shè)置名稱只是更好的區(qū)分中間點(diǎn)對(duì)象,用Connection的rollback(Savepoint savepoint)方法即可完成回滾到指定中間點(diǎn)。
JDBC對(duì)事務(wù)的支持體現(xiàn)在三個(gè)方面:
1、自動(dòng)提交模式(auto-commit mode)
Connection提供了一個(gè)auto-commit屬性來(lái)指定事務(wù)何時(shí)結(jié)束
2、當(dāng)auto-commit為true時(shí),當(dāng)每個(gè)獨(dú)立SQL操作的執(zhí)行完畢,事務(wù)立即自動(dòng)提交,也就是說(shuō)每個(gè)SQL操作都是一個(gè)事務(wù)
一個(gè)獨(dú)立SQL操作什么時(shí)候算執(zhí)行完畢,JDBC規(guī)范是這樣定義的:
對(duì)數(shù)據(jù)操作語(yǔ)言(DML)和數(shù)據(jù)定義語(yǔ)言(DDL),語(yǔ)句一執(zhí)行完就視為執(zhí)行完畢
3、當(dāng)auto-commit為false時(shí),每個(gè)事務(wù)都必須顯示調(diào)用commit方法進(jìn)行提交,或者顯示調(diào)用rollback方法進(jìn)行回滾。auto-commit默認(rèn)為true
事務(wù)隔離級(jí)別(Transaction Isolation Levels)
JDBC定義了五種事務(wù)隔離級(jí)別:
- TRANSACTION_NONE JDBC驅(qū)動(dòng)不支持事務(wù)
- TRANSACTION_READ_UNCOMMITTED 允許臟讀、不可重復(fù)讀和幻讀
- TRANSACTION_READ_COMMITTED 禁止臟讀,但允許不可重復(fù)讀和幻讀
- TRANSACTION_REPEATABLE_READ 禁止臟讀和不可重復(fù)讀,單運(yùn)行幻讀
- TRANSACTION_SERIALIZABLE 禁止臟讀、不可重復(fù)讀和幻讀
保存點(diǎn)
JDBC定義了SavePoint接口,提供一個(gè)更細(xì)粒度的事務(wù)控制機(jī)制。當(dāng)設(shè)置了一個(gè)保存點(diǎn)后,可以rollback到該保存點(diǎn)處的狀態(tài),而不是rollback整個(gè)事務(wù)
首先,我們來(lái)看看現(xiàn)有的JDBC操作會(huì)給我們帶來(lái)什么重大問(wèn)題,比如有一個(gè)業(yè)務(wù):當(dāng)我們修改一個(gè)信息后再去查詢這個(gè)信息,看似是一個(gè)簡(jiǎn)單的業(yè)務(wù),實(shí)現(xiàn)起來(lái)也非常容易,但當(dāng)這個(gè)業(yè)務(wù)放在多線程高并發(fā)的平臺(tái)下,問(wèn)題自然就出現(xiàn)了,比如當(dāng)我們執(zhí)行了一個(gè)修改后,在執(zhí)行查詢前有一個(gè)線程也執(zhí)行了修改語(yǔ)句,這時(shí)我們?cè)賵?zhí)行查詢,看到的信息就有可能與我們修改的不同。為了解決這一問(wèn)題,我們必須引入JDBC事務(wù)機(jī)制,其實(shí)現(xiàn)代碼很簡(jiǎn)單,給出示例代碼供大家參考:
- 詳解Java的MyBatis框架中的事務(wù)處理
- 詳解Java的JDBC API中事務(wù)的提交和回滾
- Java 事務(wù)詳解及簡(jiǎn)單應(yīng)用實(shí)例
- Java Spring 事務(wù)回滾詳解
- 在Java的JDBC使用中設(shè)置事務(wù)回滾的保存點(diǎn)的方法
- Java事務(wù)的個(gè)人理解小結(jié)
- Java與Oracle實(shí)現(xiàn)事務(wù)(JDBC事務(wù))實(shí)例詳解
- java事務(wù)回滾失敗問(wèn)題分析
- Java基于JDBC實(shí)現(xiàn)事務(wù),銀行轉(zhuǎn)賬及貨物進(jìn)出庫(kù)功能示例
- Java使用jdbc連接MySQL數(shù)據(jù)庫(kù)實(shí)例分析
- Java實(shí)現(xiàn)的mysql事務(wù)處理操作示例
相關(guān)文章
淺談JAVA字符串匹配算法indexOf函數(shù)的實(shí)現(xiàn)方法
這篇文章主要介紹了淺談字符串匹配算法indexOf函數(shù)的實(shí)現(xiàn)方法,indexOf函數(shù)我們可以查找一個(gè)字符串(模式串)是否在另一個(gè)字符串(主串)出現(xiàn)過(guò)。對(duì)此感興趣的可以來(lái)了解一下2020-07-07
Java使用Swing實(shí)現(xiàn)一個(gè)模擬電腦計(jì)算器
Java Swing 是一個(gè)用于創(chuàng)建 Java GUI(圖形用戶界面)的框架,它提供了一系列的 GUI 組件和工具,可以用于創(chuàng)建桌面應(yīng)用程序,包括按鈕、文本框、標(biāo)簽、表格等等,本文給大家介紹了Java使用Swing實(shí)現(xiàn)一個(gè)模擬計(jì)算器,感興趣的同學(xué)可以自己動(dòng)手嘗試一下2024-05-05
Oracle + Mybatis實(shí)現(xiàn)批量插入、更新和刪除示例代碼
利用MyBatis動(dòng)態(tài)SQL的特性,我們可以做一些批量的操作,下面這篇文章主要給大家介紹了關(guān)于Oracle + Mybatis實(shí)現(xiàn)批量插入、更新和刪除的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2018-01-01
如何解決Springboot?Dao注入失敗的問(wèn)題
這篇文章主要介紹了如何解決Spring?boot?Dao注入失敗的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05
基于springboot的flowable工作流實(shí)戰(zhàn)流程分析
這篇文章主要介紹了基于springboot的flowable工作流實(shí)戰(zhàn)流程分析,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-10-10
關(guān)于使用ContextClassLoader遇到的問(wèn)題
這篇文章主要介紹了關(guān)于使用ContextClassLoader遇到的問(wèn)題,ContextClassLoader是通過(guò)Thread.currentThread().getContextClassLoader()返回該線程上下文的ClassLoader,需要的朋友可以參考下2023-10-10
Java解析調(diào)用webservice服務(wù)的返回XML串詳解
這篇文章主要介紹了Java解析調(diào)用webservice服務(wù)的返回XML串詳解的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-07-07
Java中常用阻塞隊(duì)列的問(wèn)題小結(jié)
這篇文章主要介紹了Java常用阻塞隊(duì)列問(wèn)題,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-01-01

