[JavaEE - JPA] 1. 事務的基礎概念

現在任何應用都需要數據持久化。否則就不算是一個完整的應用。那麼對於一個數據持久化而言,最重要的無外乎兩方面:

  1. 事務管理(Transaction Management)
  2. 對象關係映射(Object Relational Mapping)

本文作爲JPA(Java Persistence API)這一系列文章的首篇,就來先談談事物管理相關的一些概念和基礎。

事務(Transaction, TX)

事務管理,事務管理,管理的是事務。那麼事務又究竟是個什麼呢。

比較標準的定義可以參考英文Wiki以及百度百科

這裏嘗試用比較好理解的方式來解釋一下什麼是事務。

我們都知道不管多複雜的代碼邏輯最終都是由一行行的代碼所組成的。這些代碼的執行順序有先有後,在一個執行單元(如今一般是線程)內部,絕對不可能出現同時執行兩個操作的情況,那麼當前一個操作出現錯誤的時候(比如拋出了異常),往往後面的操作就無法執行下去了。如果這兩個操作在邏輯上是一個整體,比如我們都知道的銀行轉帳問題,那麼問題就來了。銀行轉帳粗略可以分爲下面兩個行爲(不考慮查詢過程):

  1. 發起賬戶的金額減去轉賬金額
  2. 目標賬戶的金額加上轉賬金額

一個成功的轉賬操作,上面的兩個行爲必然都要成功。不會允許行爲1成功,而行爲2失敗的情況存在。那麼如何保證這一點呢?答案就是通過事務。

所謂的事務,實際上是一種抽象。它將一系列的細微操作組合起來形成一個整體上的操作,這個整體上的操作要麼成功,要麼失敗,不存在除此之外的任何其它狀態。因爲處於其它狀態就好比上述銀行轉賬例子中的行爲1成功,行爲2失敗這種狀態,是萬萬不可在現實的金融系統中出現的,否則世界豈不亂了套?

所以從上面的例子中,我們可以發現事務最重要的一個特點 - 原子性(Atomicity)。這個整體操作就是一個原子操作,要麼其中所有的操作所有都成功,要麼所有的都失敗。

而除了原子性之外,一般的事務還有另外3個特點:

  • 一致性(Consistency):事務結束後,參與其中的數據應該處於一種能夠解釋的通的狀態。不應該處於一種”錯亂”的狀態。比如轉賬前後,參與到過程中的兩個賬戶的總金額應該保持相等(不考慮萬惡的手續費)。

  • 隔離性(Isolation):在一個事務正在進行的過程中,對於變更只有在該事務內部纔可見。在事務成功提交之前,事務外部對於這個變更是不可見的。比如說,現在銀行系統有一個查詢轉賬次數的統計字段,在轉賬事務的過程中,肯定需要對這個字段進行+1的操作。那麼這個操作在轉賬事務未提交之前,銀行的統計程序是沒辦法得到變更後的最新數據的。只有當轉賬確確實實成功提交之後,這個最新的數據才生效,纔對外部可見。

  • 持久性(Durability):在事務內執行的變更操作在事務成功提交後仍然生效。(這個很好理解,要不豈不是白乾了)

將上面的4個特點的首字母組合起來,就是大名鼎鼎的ACID。這個ACID就是用來描述事務的特點的。雖然我們在後面的討論中會發現儘管JavaEE應用中的事務通常都是滿足ACID幾個特點的。但是隨着技術的發展和更迭,並不是所有的事務都能夠滿足ACID。尤其是現在互聯網和大數據的時代背景下,由於數據量激增,對於某些應用場景已經沒有辦法讓事務滿足所有的4個特點。往往都會根據場景的特點來犧牲掉某個特點,來換取更佳的性能讓用戶能夠滿意。

JavaEE中的事務

既然本文是作爲介紹和討論JPA的首篇文章,那麼就必然需要提及JavaEE環境下的事務。畢竟JPA也只是JavaEE整體生態環境下的一個用於描述數據持久化的規範而已。

JavaEE中的事務可以分爲兩種類型:

  1. Resource-local事務
  2. Container事務

    Resource-local事務,翻譯成中文就是”本地資源”事務。這是最基本的事務類型,直接和JDBC的DataSource接口打交道,因此本質上而言它就是數據庫事務。所以即使不在JavaEE這個環境下,比如JavaSE中也是能夠使用這種事務類型的。

而Container事務就不同了,根據名字就可以知道它是依賴於容器(Container)的,對於實現了JavaEE標準的應用服務器而言,Container事務一般指的就是使用了JTA(java Transaction API)的事務。這種事務的特點是能夠將一系列的企業級資源涵蓋到一個事務中,比如我們常見的數據庫,消息隊列等等。畢竟企業級應用比較複雜,數據可能分散到很多個數據源中,保存和修改這些數據的時候要保證它們的ACID性質還是需要一定代價的。

事務劃分(Transaction Demarcation)

我們已經知道了事務實際上是將一系列操作打了個包,形成了一個”原子”操作。那麼事務劃分所要解決的問題就是如何規定事務從哪裏開始,到哪裏結束。對於企業級應用的開發而言,事務劃分可以算是比較重要的一環,如果事務劃分的不恰當則很容易引起數據錯亂以及性能下降。

接下來我們來看看上面談到的兩種事務類型是如何劃分的。

Resource-local事務

對於這種最基本的事務類型,都是由開發人員直接通過編碼地方式來完成事務劃分。比如下面這類常見操作:

tx.begin();    // 事務開始
// ......
tx.commit();  // 事務提交
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

所以事務劃分聽上去有點嚇人,實際上放到Resource-local事務類型中來,就是兩行代碼而已(如果考慮到可能出現的tx.rollback(),則是三行代碼)。

Container事務

對於容器中的事務,除了像上面那樣由開發人員直接劃分,還多了一種選擇,就是讓容器來幫你劃分。歸納一下就是下面的兩種方案:

  1. 使用JTA接口在應用中編碼完成顯式劃分
  2. 在容器的幫助下完成自動劃分

由於JPA作爲JavaEE規範的一部分,對同屬於JavaEE規範中的EJB作了充分考慮,因此對於EJB而言,上面的兩種方案可以被具體描述爲:

對於第一種情況,利用JTA提供的接口完成的劃分,可以被稱爲基於Bean的事務(Bean-managed Transaction,BMT)。
對於第二種情況,利用容器完成事務的自動劃分的,可以被成爲基於容器的事務(Container-managed Transaction,CMT)。

而我們都知道EJB在2.x時代由於其自身十分笨重,開發效率比較低而被瘋狂吐槽。所以纔出現了時至今日都十分流行的spring Framework。好不容易到了EJB 3.x時代,雖然它提高了不少,但是由於口碑比較差加上Spring Framework的出色表現,導致真正使用EJB來進行企業級Java開發的人數還是比較少。

那麼將上面兩種方案放到Spring Framework這個語境中,是這樣描述的:

對於第一種情況,被稱爲編程式的事務管理(Programmatic Transaction Management)。
對於第二種情況,被稱爲聲明式的事務管理(Declarative Transaction Management)。

所以不要拘泥於具體的稱呼,還是要還原到問題的本質。而規範就是定義這個本質的。

總結

本篇文章首先介紹了事務是什麼,然後提到了非常著名的ACID性質。
緊接介紹了JavaEE中的事務類型以及事務劃分的概念。
在下一篇文章中將繼續介紹事務劃分在EJB以及Spring中的具體實現方式。


原文地址:http://blog.csdn.net/dm_vincent/article/details/52566964
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章