事務模板

爲何提供事務模板

截至到如今爲止,除非你使用 dao.execute(Sql ...) ,一次執行多個 SQL,是事務安全的,其餘的狀況 均是事務不安全的,好比以下代碼:html

Pet pet1 = dao.fetch(Pet.class,"XiaoBai"); Pet pet2 = dao.fetch(Pet.class,"XiaoHei"); pet1.setNickname("BaiBai"); pet2.setNickname("HeiHei"); dao.update(pet1); dao.update(pet2);

尤爲是請關注最後兩句:java

dao.update(pet1); dao.update(pet2);

當第二句話拋出異常的時候,第一句話不能被回滾。這兩條調用就是不事務安全的。若是我想讓 pet1 和 pet2 的更新操做是原子性的,它們必須一同成功,一同失敗,怎麼辦呢?sql

 

使用事務模板

Nutz.Dao 提供了簡單的解決辦法: 事務模板數據庫

 

一段示例代碼

上一節的例子能夠修改成:api

final Pet pet1 = dao.fetch(Pet.class,"XiaoBai"); final Pet pet2 = dao.fetch(Pet.class,"XiaoHei"); pet1.setNickname("BaiBai"); pet2.setNickname("HeiHei"); // Begin transaction Trans.exec(new Atom(){ public void run() { dao.update(pet1); dao.update(pet2); } }); // End transaction

提供一個 org.nutz.trans.Atom 接口的匿名實現,在裏面執行的全部的 Dao 操做都是原子性的,由於它們在 同一個 「原子 (Atom)」 裏。安全

 

事務的關鍵就是原子的界定

事務最核心的是原子的界定,在 Nutz.Dao中,界定原子的方法出奇的簡單,藉助匿名類,你能夠隨時將一段 代碼用你的原子實現包裹住。而 Trans.exec() 方法接受數目可變的原子,每一個原子都是事務性的。閉包

Trans.exec 的函數聲明oracle

public static void exec(Atom... atoms);

被原子實現包裹住的代碼就是事務安全的,不管它同時操做了多少個 DataSource。 Nutz.Dao 提供的原子接口很是簡單,實際上它就是 java.lang.Runnable 的一個別名,下面就是這個接口的 所有代碼:函數

package com.zzh.trans; public interface Atom extends Runnable {}

這幾乎是我寫過的最簡單的 Java 類了,正是由於它簡單,因此纔有無限的威力。你若是查看過 Nutz 的源代 碼包,在和數據庫操做的地方,你總會和 Atom 不期而遇。不少朋友曾經很不適應匿名類的寫法,是的,我在 早期寫 Java 的時候也比較討厭匿名類,可是熟悉了之後,你會真正喜歡上這個東西,就像你寫 Javascript 的 一段時候之後,多數人都會喜歡上「閉包」同樣。你能夠把匿名類看成 Java 給你的閉包。fetch

採用事物模板的來界定事物有一個缺點,這是 Java 語言帶來的限制:你有可能須要將一些相關的變量聲明成 final 的。 而且在 run 函數中,你只能向外拋 RuntimeException 或其子類。

 

設置事務的級別

在 JDBC 的 java.sql.Connection 接口中定義的 setTransactionIsolation 函數能夠設置事務的級別 Nutz.Dao 也提供另一個靜態函數,容許你設置事務的級別:

Trans.exec 的函數聲明

public static void exec(int level, Atom... atoms);

這裏的第一個參數 level 和 java.sql.Connection 接口中的 setTransactionIsolation 規定的 level 是同樣的。下面 是在 java.sql.Connection 裏面關於 level 參數的 JDoc 說明:

它能夠是下列常量中的任意一個值:

  • Connection.TRANSACTION_READ_UNCOMMITTED
  • Connection.TRANSACTION_READ_COMMITTED
  • Connection.TRANSACTION_REPEATABLE_READ
  • Connection.TRANSACTION_SERIALIZABLE

注意: 不能使用常量 Connection.TRANSACTION_NONE,由於它的意思是「不支持事務」

關於 level 參數的更多說明,請參看 java.sql.Connection 的文檔

不一樣的數據庫,對於 JDBC 事務級別的規範,支持的力度不一樣。請參看相應數據庫的文檔,已 肯定你設置的數據庫事務級別 是否被支持。

 

事務的嵌套

Nutz 的事務模板能夠嵌套嗎? 答案是確定的。事實上,Nutz 支持事務模板的無限層級嵌套。

這裏,若是每一層嵌套,指定的事務級別有所不一樣,不一樣的數據庫,可能引起不可預知的錯誤。因此, 嵌套的事務模板的事務,將以最頂層的事務爲級別爲標準。就是說,若是最頂層的事務級別爲 'TRANSACTION_READ_COMMITTED',那麼下面所 包含的全部事務,不管你指定什麼樣的事務級別,都是 'TRANSACTION_READ_COMMITTED', 這一點,由抽象類 Transaction 來保證。 其 setLevel 當被設置了一個大於 0 的整數之後,將再也不 接受任何其餘的值。

你能夠經過繼承 Transaction 來修改這個默認的行爲,固然,這個行爲修改通常是沒有必要的。

另外,你還可能須要知道,經過 Trans.setup 方法,能讓整個虛擬機的 Nutz 事務操做都使用你的 Transaction 實現

下面我給出兩個例子:

最外層模板決定了整個事務的級別:

Trans.exec(Connection.TRANSACTION_READ_COMMITTED, new Atom(){ public void run(){ dao.update(xxx); dao.update(bbb); // 在下層模板,雖然你指定了新的事務級別,可是這裏的事務級別仍是 // 'TRANSACTION_READ_COMMITTED'。在一個事務中,級別一旦設定就不可更改 Trans.exec(Connection.TRANSACTION_SERIALIZABLE, new Atom(){ public void run(){ dao.update(CCC); dao.update(EEE); } }); } });

讓整個函數都是事務的:

public void updatePet(final Pet pet){ Trans.exec(new Atom(){ public void run(){ dao.update(pet); dao.update(pet.getMaster()); } }); } // 在另一個函數裏,能夠這麼使用 public void updateDogAndCat(final Pet dog, final Pet cat){ Trans.exec(new Atom(){ public void run(){ updatePet(dog); updatePet(cat); } }); }
 

擴展實現

org.nutz.trans.Trans 類的 exec()方法,接受數目可變的 Atom 實例,足夠方便了吧。 可是它默認只能支 持在一臺機器上保證事務性,就是在一個 JVM 裏保證代碼的事務性。若是跨越多個JVM一塊兒組合的 Service, 如何保證事務性呢,很抱歉,Nutz.Dao 的初版的實現裏不包括跨越多個JVM依然保證事務性的功能,可是 你若是真的須要這個功能也不要緊,你能夠本身寫一個 org.nutz.trans.Transaction 的實現,而後在你的應 用啓動時,經過

org.nutz.trans.Trans.setup(你的實現類)

替換 Nutz.Dao 的默認實現。

 

總結一下 Nutz.Dao 事務

  • org.nutz.trans.Trans 類提供了兩個函數 exec
    • 一個接受數目可變的 Atom 對象
    • 一個接受一個整型值用以界定本事務的級別,以及一個數目可變的 Atom 對象
  • Atom 類就是 java.lang.Runnable 的一個別名
  • 在一個 Atom 裏,不管同時操做多少 DataSource,都是事務安全的(因爲不是使用XADataSource,沒法100%保證)
  • 你能夠經過實現本身的 Transaction 實現類,擴展 Nutz.Dao 對於事務的支持
相關文章
相關標籤/搜索