Java程式設計師從笨鳥到菜鳥之(八十)細談Spring(九)spring+hibernate宣告式事務管理詳解
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
宣告式事務管理是spring對事務管理的最常用的方式,因為這種方式對程式碼的影響最小,因此也符合非侵入性的輕量級容器的概念。Spring
下面我們就以一個spring官方文件所給的例子來具體看一下用xml配置方式怎麼來實現宣告式事務管理:
首先請看下面的介面和它的實現。這個例子的意圖是介紹概念:
// 我們想做成事務性的服務介面
package x.y.service;public interface FooService { Foo getFoo(String fooName); Foo getFoo(String fooName, String barName); void insertFoo(Foo foo); void updateFoo (Foo foo);}
// 上述介面的一個實現
package x.y.service;public class DefaultFooService implements FooService { public Foo getFoo(String fooName) { throw new UnsupportedOperationException(); } public Foo getFoo(String fooName, String barName) { throw new UnsupportedOperationException(); } public void insertFoo(Foo foo) { throw new UnsupportedOperationException(); } public void updateFoo(Foo foo) { throw new UnsupportedOperationException(); }}
首先要解釋的是很多同學可能都在考慮這個事務管理到底是放在dao層還是放在service層呢。這個問題我想大多數童鞋的反應應該都是在dao層上吧,剛開始我也是這麼想的。但是大家想想,如果我們要進行兩個甚至多個dao層中的方法操作,並且要求放在同一個事務裡時,我們該怎麼來管理這個事務呢,這時我們就沒辦法了。所以我們應該把事務管理放在service層中,我們直接在service層中呼叫這兩個dao層的方法就oK了。
下面我們接著往下看,我們假定,FooService的前兩個方法(getFoo(String) 和getFoo(String, String))必須執行在只讀事務上下文中,其他的方法(insertFoo(Foo)和 updateFoo(Foo))必須執行在可讀寫事務上下文中。我們根據這個要求來看一下配置檔案,我們剛開始可能看不懂,不用慌,往下我們會一一解釋的。
<!-- from the file 'context.xml' --><?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <!-- this is the service object that we want to make transactional --> <bean id="fooService" class="x.y.service.DefaultFooService"/> <!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean below) --> <tx:advice id="txAdvice" transaction-manager="txManager"> <!-- the transactional semantics... --> <tx:attributes> <!-- all methods starting with 'get' are read-only --> <tx:method name="get*" read-only="true"/> <!-- other methods use the default transaction settings (see below) --> <tx:method name="*"/> </tx:attributes> </tx:advice> <!-- ensure that the above transactional advice runs for any execution of an operation defined by the FooService interface --> <aop:config> <aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/> </aop:config> <!-- don't forget the DataSource --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/> <property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/> <property name="username" value="scott"/> <property name="password" value="tiger"/> </bean> <!-- similarly, don't forget the PlatformTransactionManager --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- other <bean/> definitions here --></beans>
好了,配置一大片,什麼東西,我也看不懂,呵呵,沒關係,一會大家就明白了,我們先來看一下官方給的解釋,然後我在根據我自己的理解給大家通俗的解釋一下這裡的內容。
我們要把一個服務物件('fooService' bean)做成事務性的。 我們想施加的事務語義封裝在<tx:advice/>定義中。<tx:advice/> “把所有以 'get' 開頭的方法看做執行在只讀事務上下文中, 其餘的方法執行在預設語義的事務上下文中”。 其中的 'transaction-manager' 屬性被設定為一個指向 PlatformTransactionManager bean的名字(這裡指 'txManager'), 該bean將會真正管理事務。配置中最後一段是 <aop:config/> 的定義, 它確保由 'txAdvice' bean定義的事務通知在應用中合適的點被執行。 首先我們定義了 一個切面,它匹配 FooService 介面定義的所有操作, 我們把該切面叫做 'fooServiceOperation'。然後我們用一個通知器(advisor)把這個切面與 'txAdvice' 繫結在一起, 表示當 'fooServiceOperation' 執行時,'txAdvice' 定義的通知邏輯將被執行。
好了,上面就是官方文件給出的這個配置檔案的解釋,不知道大家有沒有看懂,反正對於初學者我的時候,我是真沒看懂,不太容易懂,當然了,大牛們是一定能看懂的。下面我就根據我自己的理解來通俗的講解一下。
首先我們應該要把服務物件'fooService' 宣告成一個bean,我們要把一個服務物件('fooService' bean)做成事務性的。我們就應該首先在宣告一個事務管理的建議,用什麼來管理,spring給我們提供了事務封裝,這個就封裝在了<tx:advice/>中,這個事務建議給我們提供了一個transaction-manager屬性,用他可以指定我們用誰來管理我們的事務。我們上邊的例子用的為一個指向 PlatformTransactionManager bean的名字(這裡指 'txManager'), 該bean將會真正管理事務。上面用的事務管理類是用的jdbc中提供的事務管理,當然這裡也可以指定為hibernate管理。當然了,不管用那個類來管理我們的事務,都不要忘記了提供我們的datasource屬性,因為事務管理也需要這裡面的資訊。我們宣告好事務建議,也指定好了具體用哪個類來管理了,下面我們的任務就是要把我們定義好的這些利用AOP把我們的事務管理織入到我們的業務邏輯裡面了。<aop:config/> 的定義, 它確保由 'txAdvice' bean定義的事務通知在應用中合適的點被執行。 首先我們定義了 一個切面,它匹配 FooService 介面定義的所有操作, 我們把該切面叫做 'fooServiceOperation'。<aop:pointcut/> 元素定義是AspectJ的切面表示法,上述表示x.y.service.FooService包下的任意方法。然後我們用一個通知器(advisor)把這個切面與 'txAdvice' 繫結在一起, 表示當 'fooServiceOperation' 執行時,'txAdvice' 定義的通知邏輯將被執行。大體流程就是這樣的了。
上面的配置將為'fooService' bean建立一個代理物件,這個代理物件被裝配了事務通知,所以當它的相應方法被呼叫時,一個事務將被啟動、掛起、被標記為只讀,或者其它(根據該方法所配置的事務語義)。
我們來看看下面的例子,測試一下上面的配置。
public final class Boot { public static void main(final String[] args) throws Exception { ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml", Boot.class); FooService fooService = (FooService) ctx.getBean("fooService"); fooService.insertFoo (new Foo()); }}
執行可以清楚的看到如下結果:
- Invoking rollback for transaction on x.y.service.FooService.insertFoo
due to throwable [java.lang.UnsupportedOperationException]
<tx:advice/> 有關的設定
通過 <tx:advice/> 標籤來指定不同的事務性設定。預設的 <tx:advice/> 設定如下:
事務傳播設定是 REQUIRED
隔離級別是DEFAULT
事務是 讀/寫
事務超時預設是依賴於事務系統的,或者事務超時沒有被支援。
任何 RuntimeException 將觸發事務回滾,但是任何 checked Exception 將不觸發事務回滾
這些預設的設定當然也是可以被改變的。 <tx:advice/> 和 <tx:attributes/> 標籤裡的 <tx:method/> 各種屬性設定總結如下:
屬性 |
是否需要? |
預設值 |
描述 |
name |
是 |
與事務屬性關聯的方法名。萬用字元(*)可以用來指定一批關聯到相同的事務屬性的方法。 如:'get*'、'handle*'、'on*Event'等等。 |
|
propagation |
不 |
REQUIRED |
事務傳播行為 |
isolation |
不 |
DEFAULT |
事務隔離級別 |
timeout |
不 |
-1 |
事務超時的時間(以秒為單位) |
read-only |
不 |
false |
事務是否只讀? |
rollback-for |
不 |
將被觸發進行回滾的 Exception(s);以逗號分開。 如:'com.foo.MyBusinessException,ServletException' |
|
no-rollback-for |
不 |
不 被觸發進行回滾的 Exception(s);以逗號分開。 如:'com.foo.MyBusinessException,ServletException' |
下面我們具體來看一下事務的傳播性的幾個值:
REQUIRED:業務方法需要在一個容器裡執行。如果方法執行時,已經處在一個事務中,那麼加入到這個事務,否則自己新建一個新的事務。
NOT_SUPPORTED:宣告方法不需要事務。如果方法沒有關聯到一個事務,容器不會為他開啟事務,如果方法在一個事務中被呼叫,該事務會被掛起,呼叫結束後,原先的事務會恢復執行。
REQUIRESNEW:不管是否存在事務,該方法總彙為自己發起一個新的事務。如果方法已經執行在一個事務中,則原有事務掛起,新的事務被建立。
MANDATORY:該方法只能在一個已經存在的事務中執行,業務方法不能發起自己的事務。如果在沒有事務的環境下被呼叫,容器丟擲例外。
SUPPORTS:該方法在某個事務範圍內被呼叫,則方法成為該事務的一部分。如果方法在該事務範圍外被呼叫,該方法就在沒有事務的環境下執行。
NEVER:該方法絕對不能在事務範圍內執行。如果在就拋例外。只有該方法沒有關聯到任何事務,才正常執行。
NESTED:如果一個活動的事務存在,則執行在一個巢狀的事務中。如果沒有活動事務,則按REQUIRED屬性執行。它使用了一個單獨的事務,這個事務 擁有多個可以回滾的儲存點。內部事務的回滾不會對外部事務造成影響。它只對DataSourceTransactionManager事務管理器起效。
使用 @Transactional
除了基於XML檔案的宣告式事務配置外,你也可以採用基於註解式的事務配置方法。直接在Java原始碼中宣告事務語義的做法讓事務宣告和將受其影響的程式碼距離更近了,而且一般來說不會有不恰當的耦合的風險,因為,使用事務性的程式碼幾乎總是被部署在事務環境中。
下面的例子很好地演示了 @Transactional 註解的易用性,隨後解釋其中的細節。先看看其中的類定義:
// the service class that we want to make transactional@Transactionalpublic class DefaultFooService implements FooService { Foo getFoo(String fooName); Foo getFoo(String fooName, String barName); void insertFoo(Foo foo); void updateFoo(Foo foo);}
當上述的POJO定義在Spring IoC容器裡時,上述bean例項僅僅通過一 行xml配置就可以使它具有事務性的。如下:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <bean id="fooService" class="x.y.service.DefaultFooService"/> <tx:annotation-driven transaction-manager="txManager"/> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean></beans>
注意: 實際上,如果你用 'transactionManager' 來定義 PlatformTransactionManager bean的名字的話,你就可以忽略 <tx:annotation-driven/> 標籤裡的 'transaction-manager' 屬性。 如果 PlatformTransactionManager bean你要通過其它名稱來注入的話,你必須用 'transaction-manager' 屬性來指定它。
在多數情形下,方法的事務設定將被優先執行。在下列情況下,例如: DefaultFooService 類在類的級別上被註解為只讀事務,但是,這個類中的 updateFoo(Foo) 方法的 @Transactional 註解的事務設定將優先於類級別註解的事務設定。
@Transactional(readOnly = true)public class DefaultFooService implements FooService { public Foo getFoo(String fooName) { // do something } // these settings have precedence for this method @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW) public void updateFoo(Foo foo) { // do something }}
@Transactional 註解是用來指定介面、類或方法必須擁有事務語義的元資料。 如:“當一個方法開始呼叫時就開啟一個新的只讀事務,並停止掉任何現存的事務”。 預設的 @Transactional 設定如下:
事務傳播設定是 PROPAGATION_REQUIRED
事務隔離級別是 ISOLATION_DEFAULT
事務是 讀/寫
事務超時預設是依賴於事務系統的,或者事務超時沒有被支援。
任何 RuntimeException 將觸發事務回滾,但是任何 checked Exception 將不觸發事務回滾
這些預設的設定當然也是可以被改變的。 @Transactional 註解的各種屬性設定總結如下:
屬性 |
型別 |
描述 |
列舉型:Propagation |
可選的傳播性設定 |
|
isolation |
列舉型:Isolation |
可選的隔離性級別(預設值:ISOLATION_DEFAULT) |
readOnly |
布林型 |
讀寫型事務 vs. 只讀型事務 |
timeout |
int型(以秒為單位) |
事務超時 |
rollbackFor |
一組 Class 類的例項,必須是Throwable 的子類 |
一組異常類,遇到時 必須 進行回滾。預設情況下checked exceptions不進行回滾,僅unchecked exceptions(即RuntimeException的子類)才進行事務回滾。 |
rollbackForClassname |
一組 Class 類的名字,必須是Throwable的子類 |
一組異常類名,遇到時 必須 進行回滾 |
noRollbackFor |
一組 Class 類的例項,必須是Throwable 的子類 |
一組異常類,遇到時 必須不 回滾。 |
noRollbackForClassname |
一組 Class 類的名字,必須是Throwable 的子類 |
一組異常類,遇到時 必須不 回滾 |
在寫程式碼的時候,不可能對事務的名字有個很清晰的認識,這裡的名字是指會在事務監視器(比如WebLogic的事務管理器)或者日誌輸出中顯示的名字, 對於宣告式的事務設定,事務名字總是全限定名+"."+事務通知的類的方法名。比如BusinessService類的handlePayment(..)方法啟動了一個事務,事務的名稱是:
com.foo.BusinessService.handlePayment
給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow
你好! 這是你第一次使用 **Markdown編輯器** 所展示的歡迎頁。如果你想學習如何使用Markdown編輯器, 可以仔細閱讀這篇文章,瞭解一下Markdown的基本語法知識。新的改變
我們對Markdown編輯器進行了一些功能拓展與語法支援,除了標準的Markdown編輯器功能,我們增加了如下幾點新功能,幫助你用它寫部落格:
- 全新的介面設計 ,將會帶來全新的寫作體驗;
- 在創作中心設定你喜愛的程式碼高亮樣式,Markdown 將程式碼片顯示選擇的高亮樣式 進行展示;
- 增加了 圖片拖拽 功能,你可以將本地的圖片直接拖拽到編輯區域直接展示;
- 全新的 KaTeX數學公式 語法;
- 增加了支援甘特圖的mermaid語法1 功能;
- 增加了 多螢幕編輯 Markdown文章功能;
- 增加了 焦點寫作模式、預覽模式、簡潔寫作模式、左右區域同步滾輪設定 等功能,功能按鈕位於編輯區域與預覽區域中間;
- 增加了 檢查列表 功能。
功能快捷鍵
撤銷:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜體:Ctrl/Command + I
標題:Ctrl/Command + Shift + H
無序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
檢查列表:Ctrl/Command + Shift + C
插入程式碼:Ctrl/Command + Shift + K
插入連結:Ctrl/Command + Shift + L
插入圖片:Ctrl/Command + Shift + G
合理的建立標題,有助於目錄的生成
直接輸入1次#,並按下space後,將生成1級標題。
輸入2次#,並按下space後,將生成2級標題。
以此類推,我們支援6級標題。有助於使用TOC
語法後生成一個完美的目錄。
如何改變文字的樣式
強調文字 強調文字
加粗文字 加粗文字
標記文字
刪除文字
引用文字
H2O is是液體。
210 運算結果是 1024.
插入連結與圖片
連結: link.
圖片:
帶尺寸的圖片:
當然,我們為了讓使用者更加便捷,我們增加了圖片拖拽功能。
如何插入一段漂亮的程式碼片
去部落格設定頁面,選擇一款你喜歡的程式碼片高亮樣式,下面展示同樣高亮的 程式碼片
.
// An highlighted block var foo = 'bar';
生成一個適合你的列表
- 專案
- 專案
- 專案
- 專案
- 專案1
- 專案2
- 專案3
- 計劃任務
- 完成任務
建立一個表格
一個簡單的表格是這麼建立的:
專案 | Value |
---|---|
電腦 | $1600 |
手機 | $12 |
導管 | $1 |
設定內容居中、居左、居右
使用:---------:
居中
使用:----------
居左
使用----------:
居右
第一列 | 第二列 | 第三列 |
---|---|---|
第一列文字居中 | 第二列文字居右 | 第三列文字居左 |
SmartyPants
SmartyPants將ASCII標點字元轉換為“智慧”印刷標點HTML實體。例如:
TYPE | ASCII | HTML |
---|---|---|
Single backticks | 'Isn't this fun?' |
‘Isn’t this fun?’ |
Quotes | "Isn't this fun?" |
“Isn’t this fun?” |
Dashes | -- is en-dash, --- is em-dash |
– is en-dash, — is em-dash |
建立一個自定義列表
- Markdown
- Text-to- HTML conversion tool
- Authors
- John
- Luke
如何建立一個註腳
一個具有註腳的文字。2
註釋也是必不可少的
Markdown將文字轉換為 HTML。
KaTeX數學公式
您可以使用渲染LaTeX數學表示式 KaTeX:
Gamma公式展示 是通過尤拉積分
你可以找到更多關於的資訊 LaTeX 數學表示式here.
新的甘特圖功能,豐富你的文章
gantt
dateFormat YYYY-MM-DD
title Adding GANTT diagram functionality to mermaid
section 現有任務
已完成 :done, des1, 2014-01-06,2014-01-08
進行中 :active, des2, 2014-01-09, 3d
計劃一 : des3, after des2, 5d
計劃二 : des4, after des3, 5d
- 關於 甘特圖 語法,參考 這兒,
UML 圖表
可以使用UML圖表進行渲染。 Mermaid. 例如下面產生的一個序列圖::
這將產生一個流程圖。:
- 關於 Mermaid 語法,參考 這兒,
FLowchart流程圖
我們依舊會支援flowchart的流程圖:
- 關於 Flowchart流程圖 語法,參考 這兒.
匯出與匯入
匯出
如果你想嘗試使用此編輯器, 你可以在此篇文章任意編輯。當你完成了一篇文章的寫作, 在上方工具欄找到 文章匯出 ,生成一個.md檔案或者.html檔案進行本地儲存。
匯入
如果你想載入一篇你寫過的.md檔案或者.html檔案,在上方工具欄可以選擇匯入功能進行對應副檔名的檔案匯入,
繼續你的創作。
註腳的解釋 ↩︎