0

私のJboss7JavaEE 6 Webアプリケーションでは、次のような単純な「カテゴリ」エンティティのツリー構造を管理する必要があります。

@Entity
@Table(name="categorie")
@NamedQueries({ 
    @NamedQuery(name="selezionaTutti", query="select c from Categoria c left join fetch c.children left join fetch c.parent")
})
public class Categoria implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    private String nome;

    @OneToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
    @JoinColumn(name = "parent_id")
    private List<Categoria> children = new LinkedList<Categoria>();

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name = "parent_id",insertable=false,updatable=false)
    private Categoria parent;

... //various getter, setter, and so on
}

名前付きクエリは、最初に一度にすべての(小さな)ツリーをロードし、その後、永続コンテキストにとどまります。

次に、ツリーを「探索」したいので、ルートノードを取得して、この関数に渡します。

private List<Categoria> getAlberoCategorie(Categoria root, int profondita) {
        List<Categoria> tmpList = new ArrayList<Categoria>();
        root.setProfondita(profondita);
        if ( root.getParent() != null ) {
            tmpList.add(root);
        }
        if (!root.getChildren().isEmpty()) {
            profondita++;
            for (Categoria figlia : root.getChildren()) {
                tmpList.addAll(getAlberoCategorie(figlia,profondita)); // this line generates the stack overflow!!! 
            }
        }
        return tmpList;
    }

例外の正確なスタックトレースは次のとおりです。

java.lang.StackOverflowError org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:112)org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:137)org.hibernate.collection.internal.PersistentBag .isEmpty(PersistentBag.java:249)it.trew.data.CategoriaFacade.getAlberoCategorie(CategoriaFacade.java:59)

私のローカルマシンでは、すべてが実際に正常に動作します。小さなテストサーバーでは、カテゴリを読み取るときにクラッシュします。

どうすれば機能を改善できますか?

4

1 に答える 1

0

スタックスペースを超えています。スタックに配置するデータが多すぎます。これは、使用するローカル変数が多すぎる(ローカル変数がスタックに保持されている)か、再帰の深さが深すぎるか無限であることが原因である可能性があります。

できること:この順序で:

1)無限再帰をチェックします。これは、カテゴリエンティティがその子のリストにそれ自体またはその親または祖先を持っている場合に発生する可能性があります。この場合、スタックがいっぱいになると、例外が発生したときに中断する無限ループが発生します(無限ループが原因)。

これを確認するには、データベースを調べるか、コードをデバッグするか、再帰の深さをSystem.outに出力します(パラメーターにはすでに深さがありますprofondita)。

不定詞ループがない場合:

2)tmpListはスタックに保持されます。たぶん、そこにはあまりにも多くのスペースが必要です。戻り値ではなくパラメーターとして作成します(明らかにきれいではありませんが、使用するリソースは少なくなります)。

private void getAlberoCategorie(List<Categoria> list, Categoria root, int profondita) {
    root.setProfondita(profondita);
    if ( root.getParent() != null ) {
        list.add(root);
    }
    if (!root.getChildren().isEmpty()) {
        profondita++;
        for (Categoria figlia : root.getChildren()) {
            getAlberoCategorie(list, figlia,profondita); 
        }
    }
}

それでも役に立たず、深さが非常に深いが無限ではない場合:

3)再帰を反復で置き換えます(一般的なプログラミング手法ですが、この場合は再帰用に作成されているため、非常に醜いです)。

おそらく1)(私はそれを推測する)または2)が問題を解決するでしょう。

于 2012-05-16T11:38:10.610 に答える