Java中為了控制事務(wù)的一致性,會(huì)使用插入回滾點(diǎn)、callback方法,保證數(shù)據(jù)不被篡改,示例如下:
十載的蘆溪網(wǎng)站建設(shè)經(jīng)驗(yàn),針對(duì)設(shè)計(jì)、前端、開發(fā)、售后、文案、推廣等六對(duì)一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。全網(wǎng)營銷推廣的優(yōu)勢(shì)是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動(dòng)調(diào)整蘆溪建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。創(chuàng)新互聯(lián)公司從事“蘆溪網(wǎng)站設(shè)計(jì)”,“蘆溪網(wǎng)站推廣”以來,每個(gè)客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。
public String delete(String id) {
String ID = id;
db = new getConnection();
Connection con = db.getConnection();
try {
con.setAutoCommit(false);
db.executeUpdate("delete from helloworld where ID=" + ID); //更新操作1
db.executeUpdate("delete from helloworld _book where ID=" + ID); //更新操作2
db.executeUpdate("delete from helloworld_user where ID=" + ID); //更新操作3
con.commit();//提交JDBC事務(wù)
con.setAutoCommit(true);
db.close();
return “success”;
}
catch (Exception e) {
con.rollBack();//回滾JDBC事務(wù)
e.printStackTrace();
db.close();
return “fail”;
}
}
一、什么是Java事務(wù)
通常的觀念認(rèn)為,事務(wù)僅與數(shù)據(jù)庫相關(guān)。
事務(wù)必須服從ISO/IEC所制定的ACID原則。ACID是原子性(atomicity)、一致性(consistency)、隔離性
(isolation)和持久性(durability)的縮寫。事務(wù)的原子性表示事務(wù)執(zhí)行過程中的任何失敗都將導(dǎo)致事務(wù)所做的任何修改失效。一致性表示
當(dāng)事務(wù)執(zhí)行失敗時(shí),所有被該事務(wù)影響的數(shù)據(jù)都應(yīng)該恢復(fù)到事務(wù)執(zhí)行前的狀態(tài)。隔離性表示在事務(wù)執(zhí)行過程中對(duì)數(shù)據(jù)的修改,在事務(wù)提交之前對(duì)其他事務(wù)不可見。持
久性表示已提交的數(shù)據(jù)在事務(wù)執(zhí)行失敗時(shí),數(shù)據(jù)的狀態(tài)都應(yīng)該正確。
通俗的理解,事務(wù)是一組原子操作單元,從數(shù)據(jù)庫角度說,就是一組SQL指令,要么全部執(zhí)行成功,若因?yàn)槟硞€(gè)原因其中一條指令執(zhí)行有錯(cuò)誤,則撤銷先前執(zhí)行過的所有指令。更簡(jiǎn)答的說就是:要么全部執(zhí)行成功,要么撤銷不執(zhí)行。
既然事務(wù)的概念從數(shù)據(jù)庫而來,那Java事務(wù)是什么?之間有什么聯(lián)系?
實(shí)際上,一個(gè)Java應(yīng)用系統(tǒng),如果要操作數(shù)據(jù)庫,則通過JDBC來實(shí)現(xiàn)的。增加、修改、刪除都是通過相應(yīng)方法間接來實(shí)現(xiàn)的,事務(wù)的控制也相應(yīng)轉(zhuǎn)移到Java程序代碼中。因此,數(shù)據(jù)庫操作的事務(wù)習(xí)慣上就稱為Java事務(wù)。
二、為什么需要事務(wù)
事務(wù)是為解決數(shù)據(jù)安全操作提出的,事務(wù)控制實(shí)際上就是控制數(shù)據(jù)的安全訪問。具一個(gè)簡(jiǎn)單例子:比如銀行轉(zhuǎn)帳業(yè)務(wù),賬戶A要將自己賬戶上的1000元
轉(zhuǎn)到B賬戶下面,A賬戶余額首先要減去1000元,然后B賬戶要增加1000元。假如在中間網(wǎng)絡(luò)出現(xiàn)了問題,A賬戶減去1000元已經(jīng)結(jié)束,B因?yàn)榫W(wǎng)絡(luò)中
斷而操作失敗,那么整個(gè)業(yè)務(wù)失敗,必須做出控制,要求A賬戶轉(zhuǎn)帳業(yè)務(wù)撤銷。這才能保證業(yè)務(wù)的正確性,完成這個(gè)操走就需要事務(wù),將A賬戶資金減少和B賬戶資
金增加方到一個(gè)事務(wù)里面,要么全部執(zhí)行成功,要么操作全部撤銷,這樣就保持了數(shù)據(jù)的安全性。
三、Java事務(wù)的類型
Java事務(wù)的類型有三種:JDBC事務(wù)、JTA(Java Transaction API)事務(wù)、容器事務(wù)。
1、JDBC事務(wù)
JDBC 事務(wù)是用 Connection 對(duì)象控制的。JDBC Connection 接口( java.sql.Connection )提供了兩種事務(wù)模式:自動(dòng)提交和手工提交。 java.sql.Connection 提供了以下控制事務(wù)的方法:
public void setAutoCommit(boolean)
public boolean getAutoCommit()
public void commit()
public void rollback()
使用 JDBC 事務(wù)界定時(shí),您可以將多個(gè) SQL 語句結(jié)合到一個(gè)事務(wù)中。JDBC 事務(wù)的一個(gè)缺點(diǎn)是事務(wù)的范圍局限于一個(gè)數(shù)據(jù)庫連接。一個(gè) JDBC 事務(wù)不能跨越多個(gè)數(shù)據(jù)庫。
2、JTA(Java Transaction API)事務(wù)
JTA是一種高層的,與實(shí)現(xiàn)無關(guān)的,與協(xié)議無關(guān)的API,應(yīng)用程序和應(yīng)用服務(wù)器可以使用JTA來訪問事務(wù)。
JTA允許應(yīng)用程序執(zhí)行分布式事務(wù)處理–在兩個(gè)或多個(gè)網(wǎng)絡(luò)計(jì)算機(jī)資源上訪問并且更新數(shù)據(jù),這些數(shù)據(jù)可以分布在多個(gè)數(shù)據(jù)庫上。JDBC驅(qū)動(dòng)程序的JTA支持極大地增強(qiáng)了數(shù)據(jù)訪問能力。
如果計(jì)劃用 JTA 界定事務(wù),那么就需要有一個(gè)實(shí)現(xiàn) javax.sql.XADataSource 、
javax.sql.XAConnection 和 javax.sql.XAResource 接口的 JDBC
驅(qū)動(dòng)程序。一個(gè)實(shí)現(xiàn)了這些接口的驅(qū)動(dòng)程序?qū)⒖梢詤⑴c JTA 事務(wù)。一個(gè) XADataSource 對(duì)象就是一個(gè) XAConnection
對(duì)象的工廠。 XAConnection s 是參與 JTA 事務(wù)的 JDBC 連接。
您將需要用應(yīng)用服務(wù)器的管理工具設(shè)置 XADataSource 。從應(yīng)用服務(wù)器和 JDBC 驅(qū)動(dòng)程序的文檔中可以了解到相關(guān)的指導(dǎo)。
J2EE 應(yīng)用程序用 JNDI 查詢數(shù)據(jù)源。一旦應(yīng)用程序找到了數(shù)據(jù)源對(duì)象,它就調(diào)用 javax.sql.DataSource.getConnection() 以獲得到數(shù)據(jù)庫的連接。
XA 連接與非 XA 連接不同。一定要記住 XA 連接參與了 JTA 事務(wù)。這意味著 XA 連接不支持 JDBC
的自動(dòng)提交功能。同時(shí),應(yīng)用程序一定不要對(duì) XA 連接調(diào)用 java.sql.Connection.commit() 或者
java.sql.Connection.rollback() 。相反,應(yīng)用程序應(yīng)該使用 UserTransaction.begin()、
UserTransaction.commit() 和 serTransaction.rollback() 。
3、容器事務(wù)
容器事務(wù)主要是J2EE應(yīng)用服務(wù)器提供的,容器事務(wù)大多是基于JTA完成,這是一個(gè)基于JNDI的,相當(dāng)復(fù)雜的API實(shí)現(xiàn)。相對(duì)編碼實(shí)現(xiàn)JTA事
務(wù)管理,我們可以通過EJB容器提供的容器事務(wù)管理機(jī)制(CMT)完成同一個(gè)功能,這項(xiàng)功能由J2EE應(yīng)用服務(wù)器提供。這使得我們可以簡(jiǎn)單的指定將哪個(gè)方
法加入事務(wù),一旦指定,容器將負(fù)責(zé)事務(wù)管理任務(wù)。這是我們土建的解決方式,因?yàn)橥ㄟ^這種方式我們可以將事務(wù)代碼排除在邏輯編碼之外,同時(shí)將所有困難交給
J2EE容器去解決。使用EJB CMT的另外一個(gè)好處就是程序員無需關(guān)心JTA API的編碼,不過,理論上我們必須使用EJB。
四、三種事務(wù)差異
1、JDBC事務(wù)控制的局限性在一個(gè)數(shù)據(jù)庫連接內(nèi),但是其使用簡(jiǎn)單。
2、JTA事務(wù)的功能強(qiáng)大,事務(wù)可以跨越多個(gè)數(shù)據(jù)庫或多個(gè)DAO,使用也比較復(fù)雜。
3、容器事務(wù),主要指的是J2EE應(yīng)用服務(wù)器提供的事務(wù)管理,局限于EJB應(yīng)用使用。
五、總結(jié)
事務(wù)控制是構(gòu)建J2EE應(yīng)用不可缺少的一部分,合理選擇應(yīng)用何種事務(wù)對(duì)整個(gè)應(yīng)用系統(tǒng)來說至關(guān)重要。一般說來,在單個(gè)JDBC
連接連接的情況下可以選擇JDBC事務(wù),在跨多個(gè)連接或者數(shù)據(jù)庫情況下,需要選擇使用JTA事務(wù),如果用到了EJB,則可以考慮使用EJB容器事務(wù)。
如果滿意請(qǐng)及時(shí)采納,謝謝~
在說他們之間的區(qū)別之前,先考慮如下幾個(gè)問題:
1、getCurrentSession()與openSession()的區(qū)別?
* 采用getCurrentSession()創(chuàng)建的session會(huì)綁定到當(dāng)前線程中,而采用openSession()
創(chuàng)建的session則不會(huì)
* 采用getCurrentSession()創(chuàng)建的session在commit或rollback時(shí)會(huì)自動(dòng)關(guān)閉,而采用openSession()
創(chuàng)建的session必須手動(dòng)關(guān)閉
2、使用getCurrentSession()需要在hibernate.cfg.xml文件中加入如下配置:
* 如果使用的是本地事務(wù)(jdbc事務(wù))
property name="hibernate.current_session_context_class"thread/property
* 如果使用的是全局事務(wù)(jta事務(wù))
property name="hibernate.current_session_context_class"jta/property
以上是hibernate中一些使用,下面來說說jdbc與jta的區(qū)別:
JDBC 事務(wù)
JDBC 事務(wù)是用 Connection 對(duì)象控制的。JDBC Connection 接口( java.sql.Connection )提供了兩種事務(wù)模式:自動(dòng)提交和手工提交。
#在jdbc中,事務(wù)操作缺省是自動(dòng)提交。也就是說,一條對(duì)數(shù)據(jù)庫的更新表達(dá)式代表一項(xiàng)事務(wù)操作,操作成功后,系統(tǒng)將自動(dòng)調(diào)用commit()來提交,否則將調(diào)用rollback()來回滾。
# 在jdbc中,可以通過調(diào)用setAutoCommit(false)來禁止自動(dòng)提交。之后就可以把多個(gè)數(shù)據(jù)庫操作的表達(dá)式作為一個(gè)事務(wù),在操作完成后調(diào) 用commit()來進(jìn)行整體提交,倘若其中一個(gè)表達(dá)式操作失敗,都不會(huì)執(zhí)行到commit(),并且將產(chǎn)生響應(yīng)的異常;此時(shí)就可以在異常捕獲時(shí)調(diào)用 rollback()進(jìn)行回滾。這樣做可以保持多次更新操作后,相關(guān)數(shù)據(jù)的一致性,示例如下:
try {
conn =
DriverManager.getConnection
("jdbc:oracle:thin:@host:1521:SID","username","userpwd";
conn.setAutoCommit(false);//禁止自動(dòng)提交,設(shè)置回滾點(diǎn)
stmt = conn.createStatement();
stmt.executeUpdate(“alter table …”); //數(shù)據(jù)庫更新操作1
stmt.executeUpdate(“insert into table …”); //數(shù)據(jù)庫更新操作2
conn.commit(); //事務(wù)提交
}catch(Exception ex) {
ex.printStackTrace();
try {
conn.rollback(); //操作不成功則回滾
}catch(Exception e) {
e.printStackTrace();
}
}
JDBC 事務(wù)的一個(gè)缺點(diǎn)是事務(wù)的范圍局限于一個(gè)數(shù)據(jù)庫連接。一個(gè) JDBC 事務(wù)不能跨越多個(gè)數(shù)據(jù)庫。
JTA事務(wù)
JTA(Java Transaction API) 為 J2EE 平臺(tái)提供了分布式事務(wù)服務(wù)。
要用 JTA 進(jìn)行事務(wù)界定,應(yīng)用程序要調(diào)用 javax.transaction.UserTransaction 接口中的方法。例如:
utx.begin();
// ...
DataSource ds = obtainXADataSource();
Connection conn = ds.getConnection();
pstmt = conn.prepareStatement("UPDATE MOVIES ...");
pstmt.setString(1, "Spinal Tap");
pstmt.executeUpdate();
// ...
utx.commit();
讓我們來關(guān)注下面的話:
“用 JTA 界定事務(wù),那么就需要有一個(gè)實(shí)現(xiàn) javax.sql.XADataSource 、 javax.sql.XAConnection 和 javax.sql.XAResource 接口的 JDBC 驅(qū)動(dòng)程序。一個(gè)實(shí)現(xiàn)了這些接口的驅(qū)動(dòng)程序?qū)⒖梢詤⑴c JTA 事務(wù)。一個(gè) XADataSource 對(duì)象就是一個(gè) XAConnection 對(duì)象的工廠。 XAConnection s 是參與 JTA 事務(wù)的 JDBC 連接?!?/p>
要使用JTA事務(wù),必須使用XADataSource來產(chǎn)生數(shù)據(jù)庫連接,產(chǎn)生的連接為一個(gè)XA連接。
XA連接(javax.sql.XAConnection)和非XA(java.sql.Connection)連接的區(qū)別在于:XA可以參與JTA的事務(wù),而且不支持自動(dòng)提交。
注意:
Oracle, Sybase, DB2, SQL Server等大型數(shù)據(jù)庫才支持XA, 支持分布事務(wù)。
My SQL 連本地都支持不好,更別說分布事務(wù)了。
JTA方式的實(shí)現(xiàn)過程:
用XADataSource產(chǎn)生的XAConnection它擴(kuò)展了一個(gè)getXAResource()方法,事務(wù)通過這個(gè)方法把它加入到事務(wù)容器中進(jìn)行 管理.對(duì)于調(diào)用者來說,根本看不到事務(wù)是如果管理的,你只要聲明開始事務(wù),告訴容器我下面的操作要求事務(wù)參與了,最后告訴事務(wù)說到這兒可以提交或回滾了, 別的都是黑箱操作。
在使用JTA之前,你必須首先實(shí)現(xiàn)一個(gè)Xid類用來標(biāo)識(shí)事務(wù)(在普通情況下這將由事務(wù)管理程序來處理)。Xid包含三個(gè)元素:formatID、gtrid(全局事務(wù)標(biāo)識(shí)符)和bqual(分支修飾詞標(biāo)識(shí)符)。
下面的例子說明Xid的實(shí)現(xiàn):
import javax.transaction.xa.*;
public class MyXid implements Xid
{
protected int formatId;
protected byte gtrid[];
protected byte bqual[];
public MyXid()
{
}
public MyXid(int formatId, byte gtrid[], byte bqual[])
{
this.formatId = formatId;
this.gtrid = gtrid;
this.bqual = bqual;
}
public int getFormatId()
{
return formatId;
}
public byte[] getBranchQualifier()
{
return bqual;
}
public byte[] getGlobalTransactionId()
{
return gtrid;
}
}
其次,你需要?jiǎng)?chuàng)建一個(gè)你要使用的數(shù)據(jù)庫的數(shù)據(jù)源:
public DataSource getDataSource()
throws SQLException
{
SQLServerDataSource xaDS = new
com.merant.datadirect.jdbcx.sqlserver.SQLServerDataSource();
xaDS.setDataSourceName("SQLServer");
xaDS.setServerName("server");
xaDS.setPortNumber(1433);
xaDS.setSelectMethod("cursor");
return xaDS;
}
例1 這個(gè)例子是用“兩步提交協(xié)議”來提交一個(gè)事務(wù)分支:
XADataSource xaDS;
XAConnection xaCon;
XAResource xaRes;
Xid xid;
Connection con;
Statement stmt;
int ret;
xaDS = getDataSource();
xaCon = xaDS.getXAConnection("jdbc_user", "jdbc_password");
xaRes = xaCon.getXAResource();
con = xaCon.getConnection();
stmt = con.createStatement();
xid = new MyXid(100, new byte[]{0x01}, new byte[]{0x02});
try {
xaRes.start(xid, XAResource.TMNOFLAGS);
stmt.executeUpdate("insert into test_table values (100)");
xaRes.end(xid, XAResource.TMSUCCESS);
ret = xaRes.prepare(xid);
if (ret == XAResource.XA_OK) {
xaRes.commit(xid, false);
}
}
catch (XAException e) {
e.printStackTrace();
}
finally {
stmt.close();
con.close();
xaCon.close();
}
當(dāng)然,實(shí)際過程中,我們不需要寫這些代碼,這些代碼是JTA最終的實(shí)現(xiàn)代碼。
關(guān)于“兩步提交協(xié)議”,可以參看下面的文章:
兩階段提交(Two-Phase-Commit)協(xié)議
首先,兩階段提交(Two-Phase-Commit)事務(wù)的啟動(dòng)與常規(guī)的單階段提交(One-Phase-Commit)事務(wù)類似。接著,應(yīng)用程序/客 戶機(jī)對(duì)該兩階段提交(Two-Phase-Commit)操作中所涉及的所有數(shù)據(jù)庫執(zhí)行其修改工作?,F(xiàn)在,在最終提交該事務(wù)之前,客戶機(jī)通知參與的數(shù)據(jù)庫準(zhǔn)備提交(第 1 階段)。如果客戶機(jī)從數(shù)據(jù)庫收到一條“okay”,就發(fā)出命令向數(shù)據(jù)庫提交該事務(wù)(第 2 階段)。最后分布式事務(wù)(Distributed Transaction)結(jié)束。
上面的例子演示了如何在 Java 中使用 JTA 實(shí)現(xiàn)兩階段提交(Two-Phase-Commit)協(xié)議。在該應(yīng)用程序中,如果一個(gè)事務(wù)分支報(bào)告了錯(cuò)誤,您就要負(fù)責(zé)進(jìn)行錯(cuò)誤處理。但是“兩階段提交協(xié)議 簡(jiǎn)介”小節(jié)中提到仍然存在一個(gè)問題,那就是如果第 2 階段中一個(gè)事務(wù)分支發(fā)生故障,該怎么辦呢?
如果再次查看程序代碼,您可以看到在“第 1 階段”和“第 2 階段”之間有一個(gè)很小的時(shí)間間隔。在這一時(shí)間間隔中,出于某種理由,其中某一參與數(shù)據(jù)庫可能崩潰。如果發(fā)生了,我們將陷入分布式事務(wù)已經(jīng)部分提交的情形中。
假 定下列情形:在“第 1 階段”之后,您從 DB2 和 IDS 數(shù)據(jù)庫中都收到了“okay”。在下一步中,應(yīng)用程序成功提交了 DB2 的事務(wù)分支。接著,應(yīng)用程序通知 DB2 事務(wù)分支提交事務(wù)。現(xiàn)在,在應(yīng)用程序可以通知 IDS 事務(wù)分支提交它這一部分之前,IDS 引擎由于斷電發(fā)生崩潰。這就是一種部分提交全局事務(wù)的情形。您現(xiàn)在該怎么辦呢?
在重啟之后,DB2 和 IDS 都將嘗試恢復(fù)打開的事務(wù)分支。該引擎等待來自應(yīng)用程序的提示如何做。如果應(yīng)用程序沒有準(zhǔn)備重新發(fā)送“第 2 階段”的提交,該事務(wù)分支將被引擎所啟動(dòng)的試探性回滾中止。這是非常糟糕的,因?yàn)檫@將使該全局事務(wù)處于不一致狀態(tài)。
一種解決方案是用一個(gè)小型應(yīng)用程序連接引擎中打開的事務(wù)分支,并通知引擎提交或回滾這一打開的事務(wù)。如果您使用 IDS 作為后端,那么還有一個(gè)隱藏的 onmode 標(biāo)志,允許您結(jié)束打開的事務(wù)分支。(onmode -Z xid)。
在 DB2 UDB 中,您可以發(fā)出 LIST INDOUBT TRANSACTIONS 來獲得打開的 XA 事務(wù)的有關(guān)信息。您必須查看 DB2 Information Center 中的描述來解決該問題。
上面描述的情形是一個(gè)很好的例子,也是使用應(yīng)用程序服務(wù)器(Application Server)或事務(wù)監(jiān)控器(Transaction Monitor)的理由。在使用一個(gè)中間層服務(wù)器時(shí),就由該服務(wù)器負(fù)責(zé)保持事情正常。