我需要在playframework 1.x中挂起一个级联保存



我需要弄清楚如何将级联到playframework 1.x中的特定类扩展模型中的保存挂起。我需要挂起,这样我就可以执行一段代码,在持久化之前,使用最终来自级联到对象中的其他类的模型对象的数据字段的数据,重新计算对象上的数据字段。

.save()启动的实际进程似乎很难挂钩。我无法挂接saveAndCascade(),创建setWillBeSaved()方法也没有提供挂接,尽管playframework文档似乎建议它应该提供。

我有一个解决方案,但它似乎是一个相当"糟糕"的解决方案,涉及到一个黑客来欺骗hibernate在不该写的时候写。

破解解决方案是向对象添加一个"破解"布尔字段,并在每次加载、持久化或更新对象时切换该字段。这是为了让它永远不干净。

这样做是因为到目前为止,我能找到的唯一钩子是一个用@PrePersist和@PreUpdate注释的方法。问题是,如果hibernate不认为对象是脏的,它将不会调用带注释的方法。当一个相关对象中的数据字段发生变化,并级联到有问题的对象中时,就会产生问题,该对象应该提示重新计算,这会使干净的对象变脏,但由于hibernate认为对象是干净的,因此没有进行重新计算。

这是我正在使用的解决方案的一个简单版本(神圣桶添加了4个空格,这很烦人)。持久化以下任何一个对象都会导致对象A以正确的A值持久化。

@Entity
public class A extends Model
{
@OneToMany(mappedBy="a", cascade = CascadeType.ALL)
private List<B> bList = new ArrayList<B>();
private Integer a = 0;
private Boolean hack = true;
//algorithm that only matters to the question so far as it uses data fields from B and C.
public Integer getA()
{
Integer returnValue = 0;
for(B b : bList)
{
returnValue = returnValue + b.getB();
for(C c : b.getCList())
{
returnValue = returnValue + c.getC();
}
}
return  returnValue;
}
public void setBList(List<B> bList)
{
this.bList = bList;
}
public List<B> getBList()
{
return bList;
}
//Hibernate will not call this method if it believes the object to be clean.
@PrePersist
@PreUpdate
private void recalculateA()
{
hack = !hack; //remove the dirt and cross our fingers that hibernate won't write if the object is no longer dirty.
a = getA(); //recalculate the value of A, reflecting the changes made in the objects cascading into A.
}
//Hack to put dirt on every clean object to force hibernate to call recalculateA whenever someone tries to save A.
@PostPersist
@PostUpdate
@PostLoad
private void hack()
{
hack = !hack;
}
}

-

@Entity
public class B extends Model
{
@ManyToOne(cascade = CascadeType.ALL)
private A a;
@OneToMany(mappedBy = "b",  cascade = CascadeType.ALL)
private List<C> cList = new ArrayList<C>();
private Integer b = 0;
public List<C> getCList()
{
return cList;
}
public void setCList(List<C> cList)
{
this.cList = cList;
}
public void setA(A a)
{
this.a = a;
}
public void setB(Integer b)
{
this.b = b;
}
public Integer getB()
{
return b;
}

}

@Entity
public class C extends Model
{
@ManyToOne(cascade = CascadeType.ALL)
private B b;
private Integer c = 0;
public C(Integer c)
{
this.c = c;
}
public Integer getC()
{
return c;
}
public void setC(Integer c)
{
this.c = c;
}
public void setB(B b)
{
this.b = b;
}
}

我"必须有更好的方法来做这件事"的感觉非常刺痛。

您可以覆盖保存的根对象上的保存方法,并从那里调用计算方法。

对我来说,将业务计算隐藏到持久化过程中不是一件好事,因为你确保每次都调用它,但你将其隐藏到需要技术知识的过程中。

通过某种"calculateAndSave"方法显式调用此计算方法会使其更加清晰。

另一个解决方案是强制hibernate通过getA方法获取a值

@Entity
public class A extends Model
{
@OneToMany(mappedBy="a", cascade = CascadeType.ALL)
private List<B> bList = new ArrayList<B>();
@Transient
private Integer a = 0;
private Boolean calculated = false;
@Access(AccessType.PROPERTY)
public Integer getA()
{
if (!calculated) {
Integer returnValue = 0;
for(B b : bList)
{
returnValue = returnValue + b.getB();
for(C c : b.getCList())
{
returnValue = returnValue + c.getC();
}
}
a = returnValue;
calculated = true;
}
return a;
}
public void setBList(List<B> bList)
{
this.bList = bList;
calculated = false;
}
public List<B> getBList()
{
return bList;
}
}

但这意味着您可以在每次计算源更改时使计算标志无效,当b对象的cList更改时,这可能会很困难。

如果你的计算不那么重,你也可以避免计算的标志,并在每次调用getA时重做它——这是最简单的解决方案,但不是最高效的

最新更新