2015-05-07 35 views
1

模型結構沒有拋出:OptimisticLockException版本時,在春季啓動項目已經改變

@MappedSuperclass 
public class BaseModel<K extends Comparable> implements Serializable, Comparable<Object> { 

    private static final long serialVersionUID = 1L; 

    @Id 
    private K id; 

    @Version 
    private Integer version; 

    // getter/setter 
} 

@Entity 
public class MyEntity extends BaseModel<String> { 
    // some fields and it's getter/setter 
} 

記錄在我的數據庫爲my_entity

ID:1 版本:1 ...

以下是我的更新方法:

void update(String id, Integer currentVersion, ....) { 
    MyEntity myEntity = myRepository.findOne(id); 
    myEntity.setVersion(currentVersion); 
    // other assignments 

    myRepository.save(myEntity); 
} 

下面是調用此方法時正在觸發的查詢。

update my_entity set version=?, x=?, y=?, ... 
where id=? and version=? 

我期待OptimisticLockException時currentVersion在上述方法中通過比1等。

任何機構都可以幫助我,爲什麼我沒有得到OptimisticLockException? 我爲我的webmvc項目使用spring-boot。

+0

不應該休眠管​​理版本屬性? –

+0

執行該代碼時會發生什麼? –

+0

我的實體正在更新成功,每次執行此代碼時版本都會增加1。 –

回答

4

JPA規範的第54年1月11日指出:

一般而言,與該版 註釋指定字段或屬性不應當由應用程序進行更新。

從經驗,我可以建議,如果您嘗試手動更新版本字段,一些JPA提供者(OpenJPA是一個)實際上會拋出異常。

雖然不是嚴格意義上的回答你的問題,你可以重新的因素如下,以確保JPA提供者和JPA規範嚴格遵守之間的兼顧便攜性:

public void update(String id, Integer currentVersion) throws MyWrappedException { 
    MyEntity myEntity = myRepository.findOne(id); 

    if(currentVersion != myEntity.getVersion()){ 
     throw new MyWrappedException(); 
    } 

    myRepository.save(myEntity); 

    //still an issue here however: see below 
} 

假設你update(...)方法在運行但交易仍然有上述問題的JPA規範票據的第3.4.5節:

3.4.5 OptimisticLockException提供商實現可能推遲寫入到數據庫中,直到交易結束,當 與鎖定模式和沖洗模式設置一致。在 這種情況下,樂觀鎖定檢查可能不會發生,直到提交時間 和OptimisticLockException可能會在提交的「之前 完成」階段拋出。 如果OptimisticLockException必須 被應用程序捕獲或處理,則應用程序使用的沖洗方法應爲 以強制數據庫寫入發生。這個 將允許應用程序捕獲和處理樂觀鎖 異常。

基本上,然後,2個用戶可以提交同一個實體的併發修改。兩個線程都可以通過初始檢查,但是當更新刷新到可能在事務提交上的數據庫時,即在您的方法完成後,其中一個線程會失敗。使用JPA時更新前

public void update(String id, Integer currentVersion) throws MyWrappedException { 
    MyEntity myEntity = myRepository.findOne(id); 

    if(currentVersion != myEntity.getVersion()){ 
     throw new MyWrappedException(); 
    } 

    myRepository.save(myEntity); 

    try{ 
     myRepository.flush() 
    } 
    catch(OptimisticLockingFailureException ex){ 
     throw new MyWrappedException(); 
    } 
} 
+0

由於我的項目是spring-boot項目,我正在使用Spring JPA。如果我不會手動調用'flush()'方法,它將在我當前事務的提交時自動調用。 –

+0

是的,我知道Spring如何工作:更新將在事務提交時被刷新,即當update()完成執行時。正如JPA規範中突出顯示的部分所指出的那樣,不允許您在您的方法的機構內捕獲/處理/重新拋出那些可能只發生在flush()中的異常。但如果你對這件事不感興趣。 –

+0

這並不意味着我不感興趣,但當我在單一方法中使用多個存儲庫時,我擔心。 –

0

使用EVICT:

爲了使你可以捕捉和處理異常OptimisticLock,你的代碼都看起來像下面。我沒有得到@Version的工作。該屬性已增加,但更新具有錯誤版本屬性的對象時不會引發異常。

我唯一要做的就是首先將EVICT對象保存並保存。然後,如果版本屬性不匹配,則拋出HibernateOptimisticLockingException。

將休眠狀態的ShowSQL設置爲'true',以驗證實際更新sql以「where id =?and version =?」結尾。如果對象沒有被驅逐,更新語句只有「where id =?」,並且(由於顯而易見的原因)不起作用。

+0

你叫什麼驅逐? – pgreen2

+0

我讀的實體,並希望樂觀鎖定: this.sessionFactory.getCurrentSession()。evict(obj); –