1

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;
    }
}

私の「これを行うにはもっと良い方法がなければならない」という感覚が非常にうずきます。

4

2 に答える 2

0

別の解決策は、休止状態にgetAメソッドを介して値を取得するように強制することです。

@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が呼び出されるたびにそれをやり直すこともできます。これは最も単純なソリューションですが、最もパフォーマンスが高いわけではありません。

于 2012-11-20T06:43:51.480 に答える
0

保存するルート オブジェクトの save メソッドをオーバーライドし、そこから計算メソッドを呼び出すことができます。

私にとって、ビジネス計算を永続化プロセスに隠すことは良いことではありません。なぜなら、それが毎回呼び出されることを保証しますが、これを技術的な知識を必要とするプロセスに隠すからです。

ある種の「calculateAndSave」メソッドを介してこの計算メソッドを明示的に呼び出すと、より明確になります。

于 2012-11-19T06:49:33.513 に答える