想象一下以下两个实体。 Element
是包含一些数据的简单类:
@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
的限制,与{{1}}无关。看起来代码缺少一些辅助类,或抽象级别,或JPA注释技术,或以上所有。对于这个简单的案例,我需要一个优雅的,值得JPA手动解决方案。
任何提示,链接或代码片段都将不胜感激。
答案 0 :(得分:13)
如果你想要一个随时可用的hibernate实体版本解决方案试试hibernate-envers。它将使对象版本控制/审计变得轻而易举。 查看http://docs.jboss.org/envers/docs/index.html
上的文档 欢呼,祝你好运!答案 1 :(得分: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 :(得分:2)
您的解决方案可行,但为VersionedElement提供另一个表将是性能开销:VersionedElement除了一些外键列之外没有任何有用的数据。
我要做的只是将元素最新作为字段添加到类元素。然后,在DAO中,我将添加一些基于此字段执行查询的方法:
List<Element> getHistory(Element element)...
Element getLatest(Element element)...
JPA也支持@Version注释,但它用于乐观并发控制。它仍然可能用于跟踪版本号。
答案 3 :(得分:2)
好吧,我看到你对@Ioan Alexandru Cucu的回答发表评论
我希望它会以某种方式从sql方面得到补偿 - 涉及更少的行,更优化的查询
根据您问题中显示的映射,如果您需要检索完全初始化的VersionedElement实体,则需要执行类似这样的查询
from
VersionedElement v
inner join fetch
v.versions
inner join fetch
v.currentVersion
where
v.id = :id
如您所见,您需要两个联接来检索您的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。
因此我们可以将Element实体重写为
@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
只有一个加入
要从Element设置currentVersion属性,只需将Element转换为AbstractElement
versionedElement.setCurrentVersion((AbstractElement) element);
答案 4 :(得分:0)
您的解决方案没有任何问题,但您可能希望使用entity listeners以更优雅的方式确保实体的状态,我想是@prePersist和@preUpdate监听器。或者,也可以选择列表而不是集合。
答案 5 :(得分:-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);
}
}