spring 那種是可以的,一般指定用 @Transaction 指定一個service方法的事務(wù),在service方法里面,只有出現(xiàn)異常(這個異常是可以指定的。),導(dǎo)致這個方法沒有正常結(jié)束,這個就會數(shù)據(jù)庫回滾。你只有把 Update 和insert寫在一個方法里,然后把這個方法加上事務(wù)控制,那就可以了。這里面的實現(xiàn)邏輯大概是這樣,首先spring獲取連接, 我們在service方法里進行數(shù)據(jù)庫的操作,然后方法結(jié)束, 如果正常結(jié)束,spring提交事務(wù),釋放連接。如果異常結(jié)束 ,spring 進行回滾,(這個回滾會把這個service方法內(nèi)所有的修改操作回滾。)釋放連接。 大體的流程是這樣。至于這些邏輯在數(shù)據(jù)庫層面是如何實現(xiàn)的,我也不知道。
成都創(chuàng)新互聯(lián)公司是一家以成都網(wǎng)站建設(shè)公司、網(wǎng)頁設(shè)計、品牌設(shè)計、軟件運維、成都網(wǎng)站推廣、小程序App開發(fā)等移動開發(fā)為一體互聯(lián)網(wǎng)公司。已累計為成都玻璃貼膜等眾行業(yè)中小客戶提供優(yōu)質(zhì)的互聯(lián)網(wǎng)建站和軟件開發(fā)服務(wù)。
看你是什么事務(wù),jdbc事務(wù),還是分布式事務(wù),還是容器事務(wù)
1,編程式事務(wù)管理(jdbc的事務(wù)是綁定在connection上的)
Connection conn = null;
try
{
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:oracle:thin:@host:1521:SID","username","password");
conn.setAutoCommit(false); //取消自動提交
PreparedStatement ps = conn.prepareCall("update something");
ResultSet rs = ps.executeQuery();
conn點抗 mit(); //手動提交
}
catch (Exception e)
{
conn.rollback();
e.printStackTrace();
}
finally
{
conn.close();
}
2,聲明式事務(wù)
先在工程的application.xml配置文件中添加如下代碼,開啟事務(wù)
!-- 聲明式事務(wù)控制配置 --
tx:annotation-driven transaction-manager="txManager"/
bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
property name="datasource" ref="bassDataSource"/property
/bean
然后在你需要開啟事務(wù)的接口前面添加注解
@Transactional(rollbackFor = IOException.class)
public void add(String name) throws IOException
{
System.out.println("可以再類里和方法里面添加事務(wù)注解0~0");
throw new IOException();
}
直接調(diào)用接口方法就好
分布式事務(wù)處理(mysql貌似在5.X之后才支持) 的話,
1.可以直接使用spring+atomikos框架進行管理
參考:
就不貼測試代碼了,自己看著配置吧
2,使用JTA(Java Transaction API)進行分布式事務(wù)管理(測試代碼如下)
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
//分布式事務(wù)處理
public class transferAccount
{
@SuppressWarnings("null")
public void testTransferAccount()
{
UserTransaction userts = null;
Connection connA = null;
PreparedStatement psA = null;
InitialContext context = null;
Connection connB = null;
PreparedStatement psB = null;
try
{
//獲得事務(wù)管理對象
userts = (UserTransaction) context.lookup("java:comp/UserTransaction");
//獲取兩個數(shù)據(jù)庫
connA = getDataSourceA().getConnection();
connB = getDataSourceB().getConnection();
//開啟事務(wù)
userts.begin();
//sql語句
psA = connA.prepareStatement("我加1");
psB = connB.prepareStatement("我減1");
//執(zhí)行sql
psA.executeUpdate();
psB.executeUpdate();
//事務(wù)提交
userts點抗 mit();
} catch (Exception e)
{
try
{
userts.rollback();
} catch (IllegalStateException | SecurityException
| SystemException e1)
{
e1.printStackTrace();
}
e.printStackTrace();
}
finally
{
try
{
psA.close();
psB.close();
connA.close();
connB.close();
} catch (SQLException e)
{
e.printStackTrace();
}
}
}
public DataSource getDataSourceA()
{
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setDatabaseName("mysql");
dataSource.setServerName("server");
dataSource.setPortNumber(1433);
dataSource.setUser("test");
dataSource.setPassword("test");
return dataSource;
}
public DataSource getDataSourceB()
{
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setDatabaseName("mysql");
dataSource.setServerName("server");
dataSource.setPortNumber(1435);
dataSource.setUser("test1");
dataSource.setPassword("test1");
return dataSource;
}
}
;???事務(wù)處理是企業(yè)應(yīng)用需要解決的最主要的問題之一 J EE通過JTA提供了完整的事務(wù)管理能力 包括多個事務(wù)性資源的管理能力 但是大部分應(yīng)用都是運行在單一的事務(wù)性資源之上(一個數(shù)據(jù)庫) 他們并不需要全局性的事務(wù)服務(wù) 本地事務(wù)服務(wù)已然足夠(比如JDBC事務(wù)管理) ??? 本文并不討論應(yīng)該采用何種事務(wù)處理方式 主要目的是討論如何更為優(yōu)雅地設(shè)計事務(wù)服務(wù) 僅以JDBC事務(wù)處理為例 涉及到的DAO Factory Proxy Decorator等模式概念 請閱讀相關(guān)資料 ??? 也許你聽說過 事務(wù)處理應(yīng)該做在service層 也許你也正這樣做 但是否知道為什么這樣做?為什么不放在DAO層做事務(wù)處理 顯而易見的原因是業(yè)務(wù)層接口的每一個方法有時候都是一個業(yè)務(wù)用例(User Case) 它需要調(diào)用不同的DAO對象來完成一個業(yè)務(wù)方法 比如簡單地以網(wǎng)上書店購書最后的確定定單為例 業(yè)務(wù)方法首先是調(diào)用BookDAO對象(一般是通過DAO工廠產(chǎn)生) BookDAO判斷是否還有庫存余量 取得該書的價格信息等 然后調(diào)用CustomerDAO從帳戶扣除相應(yīng)的費用以及記錄信息 然后是其他服務(wù)(通知管理員等) 簡化業(yè)務(wù)流程大概如此:??? 注意 我們的例子忽略了連接的處理 只要保證同一個線程內(nèi)取的是相同的連接即可(可用ThreadLocal實現(xiàn))
首先是業(yè)務(wù)接口 針對接口 而不是針對類編程
public interface BookStoreManager{ ????????? public boolean buyBook(String bookId int quantity)throws SystemException; ????????? 其他業(yè)務(wù)方法 ??? }
接下來就是業(yè)務(wù)接口的實現(xiàn)類??業(yè)務(wù)對象
public class BookStoreManagerImpl implements BookStoreManager{ ???????? public boolean buyBook(String bookId)throws SystemException{ ????????????? Connection conn=ConnectionManager getConnection();//獲取數(shù)據(jù)庫連接 ????????????? boolean b=false; ????????????? ????????????? try{ ????????????????? conn setAutoCommit(false);? //取消自動提交 ????????????????? BookDAO bookDAO=DAOFactory getBookDAO(); ????????????????? CustomerDAO customerDAO=DAOFactory getCustomerDAO(); ??????????????????? //嘗試從庫存中取書 ????????????????? if(BookDAO reduceInventory(conn bookId quantity)){ ?????????????????????? BigDecimal price=BookDAO getPrice(bookId);? //取價格 ?????????????????????? //從客戶帳戶中扣除price*quantity的費用 ?????????????????????? b= ?????????????????????? CustomerDAO reduceAccount(conn price multiply(new BigDecimal(quantity)); ?????????????????????? ?????????????????????? 其他業(yè)務(wù)方法 如通知管理員 生成定單等 ??????????????????????? ?????????????????????? conn mit();?? //提交事務(wù) ?????????????????????? conn setAutoCommit(true); ????????????????? } ?????????????? }catch(SQLException e){ ????????????????? conn rollback();?? //出現(xiàn)異常 回滾事務(wù) ????????????????? con setAutoCommit(true); ????????????????? e printStackTrace(); ????????????????? throws new SystemException(e);?? ?????????????? } ?????????????? return b; ???????? } ??? }
然后是業(yè)務(wù)代表工廠 ?
public final class ManagerFactory { ????? public static BookStoreManager getBookStoreManager() { ???????? return new BookStoreManagerImpl(); ????? } ?? }
這樣的設(shè)計非常適合于DAO中的簡單活動 我們項目中的一個小系統(tǒng)也是采用這樣的設(shè)計方案 但是它不適合于更大規(guī)模的應(yīng)用 首先 你有沒有聞到代碼重復(fù)的 bad *** ell?每次都要設(shè)置AutoCommit為false 然后提交 出現(xiàn)異常回滾 包裝異常拋到上層 寫多了不煩才怪 那能不能消除呢?其次 業(yè)務(wù)代表對象現(xiàn)在知道它內(nèi)部事務(wù)管理的所有的細節(jié) 這與我們設(shè)計業(yè)務(wù)代表對象的初衷不符 對于業(yè)務(wù)代表對象來說 了解一個與事務(wù)有關(guān)的業(yè)務(wù)約束是相當(dāng)恰當(dāng)?shù)?但是讓它負(fù)責(zé)來實現(xiàn)它們就不太恰當(dāng)了 再次 你是否想過嵌套業(yè)務(wù)對象的場景?業(yè)務(wù)代表對象之間的互相調(diào)用 層層嵌套 此時你又如何處理呢?你要知道按我們現(xiàn)在的方式 每個業(yè)務(wù)方法都處于各自獨立的事務(wù)上下文當(dāng)中(Transaction Context) 互相調(diào)用形成了嵌套事務(wù) 此時你又該如何處理?也許辦法就是重新寫一遍 把不同的業(yè)務(wù)方法集中成一個巨無霸包裝在一個事務(wù)上下文中
我們有更為優(yōu)雅的設(shè)計來解決這類問題 如果我們把Transaction Context的控制交給一個被業(yè)務(wù)代表對象 DAO和其他Component所共知的外部對象 當(dāng)業(yè)務(wù)代表對象的某個方法需要事務(wù)管理時 它提示此外部對象它希望開始一個事務(wù) 外部對象獲取一個連接并且開始數(shù)據(jù)庫事務(wù) 也就是將事務(wù)控制從service層抽離 當(dāng)web層調(diào)用service層的某個業(yè)務(wù)代表對象時 返回的是一個經(jīng)過Transaction Context外部對象包裝(或者說代理)的業(yè)務(wù)對象 此代理對象將請求發(fā)送給原始業(yè)務(wù)代表對象 但是對其中的業(yè)務(wù)方法進行事務(wù)控制 那么 我們?nèi)绾螌崿F(xiàn)此效果呢?答案是JDK 引進的動態(tài)代理技術(shù) 動態(tài)代理技術(shù)只能代理接口 這也是為什么我們需要業(yè)務(wù)接口BookStoreManager的原因 ??? 首先 我們引入這個Transaction Context外部對象 它的代碼其實很簡單 如果不了解動態(tài)代理技術(shù)的請先閱讀其他資料
lishixinzhi/Article/program/Java/gj/201311/27765
import java lang reflect InvocationHandler; import java lang reflect Method; import java lang reflect Proxy;
import java sql Connection;
import strutslet demo service SystemException;
public final class TransactionWrapper {
/** ???? * 裝飾原始的業(yè)務(wù)代表對象 返回一個與業(yè)務(wù)代表對象有相同接口的代理對象 ???? */ ??? public static Object decorate(Object delegate) { ??????? return Proxy newProxyInstance(delegate getClass() getClassLoader() ??????????????? delegate getClass() getInterfaces() new XAWrapperHandler( ??????????????????????? delegate)); ??? } ??? ??? //動態(tài)代理技術(shù) ??? static final class XAWrapperHandler implements InvocationHandler { ??????? private final Object delegate;
XAWrapperHandler(Object delegate) { ?????????? this delegate = delegate; ??????? } ??????? ??????? //簡單起見 包裝業(yè)務(wù)代表對象所有的業(yè)務(wù)方法 ??????? public Object invoke(Object proxy Method method Object[] args) ??????????????? throws Throwable { ??????????? Object result = null; ??????????? Connection con = ConnectionManager getConnection(); ??????????? try { ??????????????? //開始一個事務(wù) ??????????????? con setAutoCommit(false); ??????????????? //調(diào)用原始業(yè)務(wù)對象的業(yè)務(wù)方法 ??????????????? result = method invoke(delegate args); ??????????????? con mit();?? //提交事務(wù) ??????????????? con setAutoCommit(true); ??????????? } catch (Throwable t) { ??????????????? //回滾 ??????????????? con rollback(); ??????????????? con setAutoCommit(true); ??????????????? throw new SystemException(t); ??????????? }
return result; ??????? } ??? } }
正如我們所見 此對象只不過把業(yè)務(wù)對象需要事務(wù)控制的業(yè)務(wù)方法中的事務(wù)控制部分抽取出來而已 請注意 業(yè)務(wù)代表對象內(nèi)部調(diào)用自身的方法將不會開始新的事務(wù) 因為這些調(diào)用不會傳給代理對象 如此 我們?nèi)コ舜碇貜?fù)的味道 此時 我們的業(yè)務(wù)代表對象修改成
public class BookStoreManagerImpl implements BookStoreManager { ??? public boolean buyBook(String bookId)throws SystemException{ ????????? Connection conn=ConnectionManager getConnection();// 獲取數(shù)據(jù)庫連接 ????????? boolean b=false; ????????? try{ ????????????? BookDAO bookDAO=DAOFactory getBookDAO(); ????????????? CustomerDAO customerDAO=DAOFactory getCustomerDAO(); ????????????? // 嘗試從庫存中取書 ????????????? if(BookDAO reduceInventory(conn bookId quantity)){ ????????????????? BigDecimal price=BookDAO getPrice(bookId);? // 取價格 ????????????????? // 從客戶帳戶中扣除price*quantity的費用 ????????????????? b= ????????????????? CustomerDAO reduceAccount(conn price multiply(new BigDecimal(quantity)); ????????????????? ????????????????? 其他業(yè)務(wù)方法 如通知管理員 生成定單等 ????????????????? ????????????? } ????????? }catch(SQLException e){ ???????????? throws new SystemException(e); ????????? } ????????? return b; ??? } ??? }
可以看到 此時的業(yè)務(wù)代表對象專注于實現(xiàn)業(yè)務(wù)邏輯 它不再關(guān)心事務(wù)控制細節(jié) 把它們?nèi)课薪o了外部對象 業(yè)務(wù)代表工廠也修改一下 讓它返回兩種類型的業(yè)務(wù)代表對象
public final class ManagerFactory { ????? //返回一個被包裝的對象 有事務(wù)控制能力 ????? public static BookStoreManager getBookStoreManagerTrans() { ????????? return (BookStoreManager) TransactionWrapper ????????????????? decorate(new BookStoreManagerImpl()); ????? } ????? //原始版本 ????? public static BookStoreManager getBookStoreManager() { ???????? return new BookStoreManagerImpl(); ????? } ????? ?? }
我們在業(yè)務(wù)代表工廠上提供了兩種不同的對象生成方法 一個用于創(chuàng)建被包裝的對象 它會為每次方法調(diào)用創(chuàng)建一個新的事務(wù) 另外一個用于創(chuàng)建未被包裝的版本 它用于加入到已有的事務(wù)(比如其他業(yè)務(wù)代表對象的業(yè)務(wù)方法) 解決了嵌套業(yè)務(wù)代表對象的問題 ?? 我們的設(shè)計還不夠優(yōu)雅 比如我們默認(rèn)所有的業(yè)務(wù)代表對象的方法調(diào)用都將被包裝在一個Transaction Context 可事實是很多方法也許并不需要與數(shù)據(jù)庫打交道 如果我們能配置哪些方法需要事務(wù)聲明 哪些不需要事務(wù)管理就更完美了 解決辦法也很簡單 一個XML配置文件來配置這些 調(diào)用時判斷即可 說到這里 了解spring的大概都會意識到這不正是聲明式事務(wù)控制嗎?正是如此 事務(wù)控制就是AOP的一種服務(wù) spring的聲明式事務(wù)管理是通過AOP實現(xiàn)的 AOP的實現(xiàn)方式包括 動態(tài)代理技術(shù) 字節(jié)碼生成技術(shù)(如CGLIB庫) java代碼生成(早期EJB采用) 修改類裝載器以及源代碼級別的代碼混合織入(aspectj)等 我們這里就是利用了動態(tài)代理技術(shù) 只能對接口代理 對類的動態(tài)代理可以使用cglib庫 ??? 這篇短文只是介紹下我對事務(wù)上下文模式以及聲明式事務(wù)管理實現(xiàn)基本原理的理解 如有錯誤 請不吝賜教 謝謝 我的email:killme @gmail
lishixinzhi/Article/program/Java/gj/201311/27764