2008-10-08 85 views
2

看起來像我見過的大多數JPA/Hibernate實體bean類的示例都沒有明確的同步。然而,在構建事務的上下文中,可以在這些對象上調用getters/setter。而且這些方法可以跨多個線程調用(儘管也許這是不尋常的和奇怪的)。JPA/Hibernate實體類和同步的最佳實踐是什麼?

看起來如果它是跨多個線程構建的,那麼對對象狀態的更改可能會丟失,這很可悲。

那麼,是不是同步最佳做法? Hibernate檢測代碼是否爲我處理正確的同步?

舉個例子:

@Entity 
public class Ninja { 
    @Id @GeneratedValue 
    private Long id; 

    @Column 
    private String name; 

    @Column 
    private int throwingStars; 

    public Ninja() {} 
    public int getThrowingStars() { return throwingStars; } 
    public void addThrowingStar() { throwingStars += 1; } 
} 

執行投擲星方法都需要同步?我當然不希望我的忍者失去任何投擲的星星。

+0

」[[]]對象狀態的改變會丟失,這會很難過。「 < - 我喜歡那種輕描淡寫(「會很傷心」):-) – dertoni 2012-02-13 09:06:25

回答

1

2個線程的對象可能不一樣。假設你的線程使用會話工廠來訪問數據庫,你從會話中得到的對象應該被看作是「獨立的」(我相信hibernate會爲每個get()創建'新鮮'對象,除非它們在會話中)。

至於你的恆星問題,當兩個人從DB獲取同一行時,DB'ACID'屬性會確保每個操作都是原子的,所以如果你從Thread中的忍者中移除一個星形t1和提交,線程t2將讀取提交的t1值。

或者,您可以讓hibernate在T1中鎖定相關忍者的行,這樣即使T2詢問行,也必須等到T1提交或中止。

2

「這些方法可能被跨多個線程調用(儘管也許這是不尋常的和奇怪的)。」

因爲我對JPA沒有多少經驗,所以我只能說休眠。 你調用set的對象並不總是你調用get的同一個對象。當Hibernate爲你加載對象時,它調用set *方法,然後你(和你的線程)調用剩下的時間。當你(即你的作者線程)修改一個已存在的對象並再次保存它時,你需要保護對該對象的訪問(以便其他讀寫器線程不讀取髒數據) 。

+0

所以你期待在後面的例子中看到實體類中的同步?或者您是否期望在應用程序協調的更高級別上看到同步? – 2008-10-08 18:00:57

0

JPA/Hibernate實體是POJO。 Hibernate和任何JPA提供程序不會更改運行時語義。

所以,如果你有一個簡單的POJO的併發問題,你也將它們與你的實體!

在我看到的所有系統中,域模型不是線程化的,實體實例不能被多個線程訪問。

但是,您可以同時擁有多個相同實體的實例。在這種情況下,通過數據庫進行同步。這裏的模式是樂觀和悲觀鎖定。 Hibernate和JPA可以幫助你實現這些模式。

0

您的問題最好的做法是樂觀鎖定:

從Java持久性API(JPA)規範(第3.4.1):

樂觀鎖是 用於技術確保只有當 沒有干預事務已經更新 實體狀態的數據( ,因爲 實體狀態被讀取)時纔對對應於實體的 狀態的數據庫數據進行更新。此 可確保更新或刪除 數據與數據庫的當前狀態一致,並且不會丟失干預更新。

您需要將@Version註釋添加到您的類和DB表中的一列。

3

在我看來,你永遠不應該在線程間共享域對象。事實上,我通常在線程之間共享很少的數據,因爲這些數據必須得到保護我已經建立了一些大型/高性能系統,並且從來不必違反這一規則。如果您需要並行工作,那麼請這樣做,但不要通過共享域對象的實例。每個線程都應該從數據庫讀取數據,通過變異/創建對象對其進行操作,然後提交/回滾事務。工作進出通常應該是有價值的/只讀的對象。 「