Playframework1.xの特定のクラス拡張モデルにカスケード保存をフックする方法を理解する必要があります。オブジェクトにカスケードされる他のクラスのモデルオブジェクトに属するデータフィールドから最終的に取得されるデータを使用して、永続化する前にオブジェクトのデータフィールドを再計算するコードを実行できるように、フックが必要です。
.save()によって開始される実際のプロセスは、フックに対して非常に耐性があるようです。saveAndCascade()をフックすることができず、setWillBeSaved()メソッドを作成しても、PlayFrameworkのドキュメントでフックが必要であると示唆されていても、フックは提供されませんでした。
私には解決策がありますが、それは、休止状態をだまして、必要のないときに書き込みを行うハックを伴う、かなり「悪い」解決策のようです。
ハックの解決策は、オブジェクトに「ハック」ブールフィールドを追加し、オブジェクトがロード、永続化、または更新されるたびにフィールドを切り替えることです。これは、それが決してきれいにならないようにするためです。
これが行われるのは、これまでに見つけた唯一のフックが、@PrePersistと@PreUpdateで注釈が付けられたメソッドであるためです。問題は、オブジェクトがダーティであると信じていない場合、hibernateが注釈付きメソッドを呼び出さないことです。これは、問題のオブジェクトにカスケードされる関連オブジェクトの1つでデータフィールドが変更され、クリーンなオブジェクトをダーティにする再計算を促す場合に問題を引き起こしますが、休止状態ではオブジェクトがクリーンであると見なされるため、再計算は行われません。
これが私が使用しているソリューションの単純なバージョンです(聖なるバケツは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;
}
}
私の「これを行うにはもっと良い方法がなければならない」という感覚が非常にうずきます。