2011-03-24 58 views
5

想象一下下面的兩個實體。 Element是包含一些數據的簡單類:JPA - 「版本」的實體,諮詢設計需要

@Entity 
public class Element { 
    private String data; 

    public String getData() { return data; }  
    public void setData(String data) { this.data = data; } 
} 

下節課,命名爲VersionedElement,擴展Element幷包含最新版本以及不同的版本。這是我的「解決方案」:

@Entity 
public class VersionedElement extends Element { 
    private Set<Element> versions; 
    private Element currentVersion; 

    @Override 
    public String getData() { 
     return getCurrentVersion().getData(); 
    } 

    @Override 
    public void setData(String data) { 
     getCurrentVersion().setData(data); 
    } 

    @OneToMany 
    public Set<Element> getVersions() { 
     return versions; 
    } 

    public void setVersions(Set<Element> versions) { 
     this.versions = versions; 
    } 

    @ManyToOne 
    public Element getCurrentVersion() { 
     return currentVersion; 
    } 

    public void setCurrentVersion(Element currentVersion) { 
     this.currentVersion = currentVersion; 
    } 
} 

而且我不喜歡我寫的東西,它有什麼問題,太直接了當。首先,在後一類currentVersion不被限制,也沒有關係versions。看起來代碼缺少一些輔助類,或抽象層,或JPA註釋技術,或上述所有。我需要一個優雅的,值得爲這個簡單情況的JPA手動解決方案。 任何提示,鏈接或代碼片斷,將不勝感激。

+0

是不是您當前版本的'這個'實例? – ThomasRS 2011-03-29 10:57:49

+0

@Thomas,非常好的問題,在這種情況下,特別是在更改當前版本時,怎麼樣?我不知道。可能需要重寫類,將數據作爲單獨的類取出等。但是不同的數據必須有不同的ID,這是肯定的。我有點困惑 - 我期待着「隱藏你的代碼,這樣做,那是它被用來完成的方式,這是常見模式」。不能相信沒有人已經做到了。 – Osw 2011-03-29 11:28:35

回答

13

嘗試冬眠,envers。它將使對象版本/審計變得輕而易舉。 檢查文檔在http://docs.jboss.org/envers/docs/index.html

喝彩,祝你好運!

+0

順便說一下,請查看http://www.jboss.org/envers上的jboss envers介紹頁面,以便快速查看解決方案 – 2011-03-28 15:40:18

+0

是的,它確實存在,它非常棒,它是hibernate核心的內部部分(從3.5開始);) – 2011-03-30 13:39:57

+0

+100鏈接!有點超出範圍,但會爲我的其他項目節省大量時間。 – Osw 2011-03-31 20:53:09

2

您的解決方案的工作,但具有用於VersionedElement另一臺將是一個性能開銷:VersionedElement將有除了一些外鍵列中沒有有用的數據。

我會做的是簡單地增加元最新爲域類Element。然後,在DAO,我想補充一些方法,其執行基於本場查詢:

List<Element> getHistory(Element element)... 
Element getLatest(Element element)... 

JPA還支持@Version註釋,但那是用於樂觀併發控制。它仍然可能用於跟蹤版本號。

如果你想有一個現成的岩石休眠實體版本的解決方案
+0

感謝您的回答。這就是我現在做的方式,它正在工作。當然,我要求的理想設計將會對Hibernate的性能產生影響,但我希望它能夠以某種方式從sql方面得到補償,包括更少的行,更優化的查詢等。至少,我沒有任何其他設計比較它們。 – Osw 2011-03-28 06:48:30

-1

你可以只保存在一個單獨的表到最新的實體的引用(一個行)。喜歡的東西:

@Entity 
public class CurrentElement { 

    @OneToOne   
    private Element currentVersion; 

    public static Element getCurrentVersion(EntityManager em) { 
     return em.createQuery("select x from Element x ").getSingleResult().currentVersion; 
    } 

    public static void setCurrentVersion(EntityManager em, Element newVersion) { 
     em.remove(getCurrentVersion(em)); 
     CurrentElement ce = new CurrentElement(); 
     ce.currentVersion = newVersion; 
     em.persist(ce); 
    } 
} 
0

有什麼不對您的解決方案是,但你可能想使用entity listeners,以確保一個更優雅的方式你的實體的狀態,我想用@PrePersist和@PreUpdate聽衆。或者,也可以去一個列表而不是一個列表。

4

Element可以具有一個整數字段version在對象Element本身,作爲行的運行計數,並通過順序地更新。當你想要最新的時候,你只需要按這個字段降序排列並獲取第一個結果。

@Entity 
@NamedQueries({ 
    @NamedQuery(name="GetHistory", query = "FROM Element e WHERE e.id = :id"), 
    @NamedQuery(name="GetLatest", query = "FROM Element e \ 
             WHERE e.id = :id order by e.version"), 
}) 
public class Element { 
    private String data; 

    @GeneratedValue(strategy = GenerationType.SEQUENCE, 
        generator = "SEQ_ELEMENT_VERSION") 
    private int version; 
    private int id; 


    public String getData() { return data; }  
    public void setData(String data) { this.data = data; } 
} 
2

好了,我看到@Ioan亞歷簇簇的回答您的評論

我希望它會以某種方式從SQL側進行補償 - 更少的行參與其中,更優化的查詢

根據您的問題中顯示的映射,如果您需要檢索完全初始化的VersionedElement實體,則需要執行一個查詢像這樣的

from 
    VersionedElement v 
inner join fetch 
    v.versions 
inner join fetch 
    v.currentVersion 
where 
    v.id = :id 

RY正如你看到的,你需要2加入檢索您VersionedElement實體。但Element以及VersionedElement 共享數據屬性。爲了避免重複的代碼,我們可以定義包含在兩個實體所需要的數據的抽象類,如下

@MappedSuperclass 
public abstract class AbstractElement { 

    private String data; 

    public String getData() { return data; } 
    public void setData(String data) { this.data = data; } 

} 

的JPA 1.0規範是直截了當

兩個抽象和具體類可以是實體。實體可以擴展非實體類以及實體類,非實體類可以擴展實體類。

我們需要@MappedSuperclass註解,因爲它的映射信息應適用 於從它繼承的實體。在我們的例子中,Element和VersionedElement。

所以我們可以重新寫的元素實體

@Entity 
public class Element extends AbstractElement {} 

,避免內連接抓取v.currentVersion,爲什麼不通過AbstractElement提供的數據存儲爲@Embedded屬性,而不是一個@ManyToOne屬性?

@Embeddable 
public class ElementAsEmbeddable extends AbstractElement {} 

@Entity 
public class VersionedElement { 

    private ElementAsEmbeddable currentElement; 

    private Set<Element> versions; 

    @Embedded 
    public ElementAsEmbeddable getCurrentVersion() { return currentVersion; } 
    public void setCurrentVersion(ElementAsEmbeddable currentVersion) { this.currentVersion = currentVersion; } 

    @OneToMany 
    public Set<Element> getVersions() { return versions; } 
    public void setVersions(Set<Element> versions) { this.versions = versions; } 

} 

現在你的查詢應該看起來像

from 
    VersionedElement v 
inner join fetch 
    v.versions 
where 
    v.id = :id 

只有一個連接

從一個元素建立CURRENTVERSION財產,你只需要投元素作爲AbstractElement

versionedElement.setCurrentVersion((AbstractElement) element); 
+0

看起來很有趣。但是,思考類似的方式,是什麼阻止我們將數據類設置爲完整的@Entity而不是@MappedSuperClass? – Osw 2011-03-31 20:13:04

+0

@Osw @Embeddable - 需要避免兩個連接 - 不能擴展一個實體,因爲@Embeddable本身**不是一個普通的實體**。 – 2011-03-31 20:32:01

+0

我理解你關於sql優化的觀點,但除此之外,對於使它成爲獨立實體你有什麼看法?這似乎是合理的,接近現實生活:不同的數據 - >不同的實體 - >不同的ID。 – Osw 2011-03-31 20:41:41