2017-09-21 76 views
0

我的應用程序使用SpringBoot,Hibernate和Spring Data JPA來實現魔術。我試圖將Hibernate的Envers庫添加到這個版本上,以進行修訂跟蹤。但是,我遇到了Envers和@Version註釋之間的一些衝突。在Envers中使用@Version

默認情況下,Envers不會審覈用@Version註釋的任何字段。這對我來說很有意義,因爲@Version只是爲了跟蹤樂觀鎖定,所以我並不需要保留它的修訂歷史記錄。

但是,我遇到的問題是,當我得到一個修訂實體時,它不會回來的版本。這是有道理的 - Envers將修訂實體存儲在..._ AUD表中,並且該表沒有版本列,所以顯然該實體不具有版本。問題是我有時想使用該實體作爲另一個事務*的一部分,但因爲它沒有版本,實體被認爲是暫時的,並且引發異常。

所以我有一些不同的解決方案,但他們都不理想,所以我希望得到的最好的方式輸入一些我可以做到這一點:

  1. 我可以得到修正實體,然後使用修訂實體中的id進行單獨的存儲庫調用以獲取實際實體,然後使用該實體。這會增加很多複雜性(尤其是在確定使用正確的存儲庫時)以及看起來不必要的額外數據庫調用。

  2. 我可以保存版本。這並不理想,因爲它不是我真正關心的信息,但如果它意味着一個有效的工作系統,我可以處理它。我也試過這個,並且遇到了問題(在我的配置中將doNotAuditOptimsticLockingField設置爲true似乎什麼也不做),但如果確定這是最好的解決方案,我會打開一個單獨的問題來處理它。

  3. 我可能會更改未能使用ID而非實際實體的存儲庫調用,但如果必須更改一些其他存儲庫調用以使其工作,我不會感到驚訝。

如果有第四種選擇,我全部耳朵!

*:另一個事務是將實體轉換爲由REST端點返回的資源時發生的數據庫調用。我有一個實體是AccountEntity。修訂實體我得到的是使用這種方法:

LinkOwnerEntity findByAccountEntity(AccountEntity accountEntity); 

而且linkOwner實體類看起來是這樣的:

public class LinkOwnerEntity { 

    ... 

    @ManyToOne(fetch = FetchType.LAZY) 
    @JoinColumn(name = "ACCOUNT_ID", unique=true, foreignKey = @ForeignKey(name = "FK_OWNER_ACCOUNT_ID")) 
    private AccountEntity accountEntity; 

    ... 

} 

回答

0

樂觀鎖是所有關於確保您看到的數據是數據你會修改。因此,當用戶決定在他的屏幕上恢復舊版本(我想這就是你想要用Envers做的)時,他應該看到他想要恢復的內容的當前版本和舊版本的內容。您需要將用戶看到的版本字段保留爲當前版本,並將其發送到您的請求中並將其用於保存修改。

這樣,如果數據在用戶查看後被修改,他的修改將被拒絕而不是覆蓋其他修改。

+0

我對這裏所需功能的理解只是用戶需要讀取數據,而不是修改或恢復數據。這將通過通常可用的途徑單獨完成。儘管如此,你確實很重要,我會驗證這個功能是不需要的。但是,如果不需要,我認爲Version字段沒有其他需要。 – EaterOfFromage

+0

用戶代碼實際上不應該習慣於使用'@ Version'屬性來做任何事情。 – Naros

+0

如果這裏的最終目標是恢復一個實體,那應該歸結爲首先要求Hibernate提供一個基於PK的管理實體(如果一個實體仍然存在),然後使用該PK來請求Envers進行特定修訂。然後OP需要將所有_changed_屬性覆蓋到託管實例,然後合併。如果實體已經被刪除並且不使用自然鍵,那麼版本的概念在這裏是不相關的,因爲新實例將被分配一個新的代理鍵並且將被完全視爲新的實體。 – Naros

0

在我深入瞭解我應該做的事情之前,我想澄清一些事情。

首先,您確實不應該在任何情況下將Envers Query API爲您提供的實例視爲分離的實體實例。

根據您對實體進行註釋的方式,您從Envers中檢索到的實例很可能是已審計的歷史行數據和當前行表數據的混合。實例是真實實體實例管理的屬性的子集也是可行的,因爲您可能只選擇跟蹤特定屬性。

因此,我不會親自考慮使用該檢索到的實例作爲我與事務內的託管實體相關聯的東西,至少不是盲目的。其次,從你的問題來看,不一定非常清楚的是首先查詢Envers的真正目的,然後將結果作爲映射中的關聯使用,而不是簡單地對第一個實體進行查詢地點。

所以最終,這使我想我的建議,這將是你的選擇3或其變種。

這聽起來像你可能想使用Session#loadEntityManager#getReference。這兩種方法旨在基於給定的標識符構建代理實例,然後您可以在關聯映射期間盲目使用該代理,而無需直接爲實例提供保護(從而避免數據庫調用開銷)的開銷。

這種方法還避免了任何需要使用Envers Query API的情況,因爲根據您在問題中提供的內容,我完全無法在此用例中看到它的相關性。如果有涉及Envers查詢的特定原因,請更新您的問題。