8

リンク テーブルの追加の列と多対多の関係があります。私は、所有側が熱心に子をフェッチするように構成しました (したがって、私は取得しませんLazyInitializationException)。反対方向では怠惰です。これは機能します。

私は今、トランザクションを微調整したいと思っていました ( @TransactionalDAO クラスとサービス クラスのクラス レベルだけになる前に。私はメソッドgetByIdを次のように設定しましたreadOnly = true:

@Transactional(readOnly  = true)
public Compound getById(Long id) {
    return compoundDAO.getById(id);
}

この変更の後LazyInitializationException、次のスニペットを取得します。

Compound compound = compoundService.getById(6L);        
Structure structure = compound.getComposition().get(0).getStructure();
System.out.println("StructureId: "+ structure.getId()); // LazyInitializationException

これを削除すると(readOnly = true)機能します!誰でもこの動作を説明できますか? 私はSpring + Hibernateを使用しています。これがロードされるデータに影響を与える理由がわからないので、ややこしいですか?


編集:

関係定義のスニペット。これは、リンク テーブルに列がある多対多です。

所有側 (例: 化合物に構造が含まれる):

@OneToMany(fetch = FetchType.EAGER, mappedBy = "pk.compound",
    cascade = CascadeType.ALL, orphanRemoval = true)
@OrderBy("pk.structure.id ASC")
private List<CompoundComposition> composition = new ArrayList<>();

側に属します:

@OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.structure",
cascade = CascadeType.ALL)
@OrderBy("pk.compound.id ASC")
private List<CompoundComposition> occurence;

@Embeddable ID クラスの多対一

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Compound getCompound() {
    return compound;
}

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Structure getStructure() {
    return structure;
}

編集2:

スタックトレース

org.hibernate.LazyInitializationException: could not initialize proxy - no Session
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:165) ~[hibernate-core-4.1.7.Final.jar:4.1.7.Final]
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:272) ~[hibernate-core-4.1.7.Final.jar:4.1.7.Final]
    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185) ~[hibernate-core-4.1.7.Final.jar:4.1.7.Final]
    at org.bitbucket.myName.myApp.entity.Structure_$$_javassist_0.getId(Structure_$$_javassist_0.java) ~[classes/:na]
    at org.bitbucket.myName.myApp.App.main(App.java:31) ~[classes/:na]

編集3:

私のコメントも参照してください:

ログは readOnly とは大きく異なり、リレーションが読み込まれた部分が欠落しています。ログにいくつかの選択がありません。

編集4:

したがって、基本的な DriverManagerDataSource と接続プールがないことにうんざりしていました。問題はまったく同じです。私にとっては、休止状態の問題のように見えます。

4

3 に答える 3

6

これはすごいです。一部の人々がORMを嫌う理由を理解し始めています...奇妙な問題を解決するために常に何時間も費やさなければならないような気がします.解決策は非常に具体的な注釈のセット+上記の制限を回避するためのコードです.注釈。

最初に、なぜこれが起こるのか(なぜ、どの注釈で意味があるのか​​ 、論理的な意味をなすという意味ではありません。常識を使用することは役に立たないので、ここで実際の問題です。試行錯誤のみが役立ちます)。所有側では、@OneToMany に orphanRemoval = true があります (これは一貫性のために必要であることがわかりました。データベースの制約がそれを処理する必要があると考える人もいるでしょう...あなたを夢中にさせる可能性のある多くのことの 1 つにすぎません)。トランザクションが読み取り専用ではない場合、この設定により一部のデータがフェッチされるようになるため、遅延が発生するようです。つまり、次のようになります。

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Structure getStructure() {
    return structure;
}

読み取り専用トランザクションでは、このフェッチは行われません。何も変更できない場合は、孤立したものを削除する必要がないため、この設定の背後にあるロジックが必要とするデータは読み取り専用の tx では必要ないためだと思います。

したがって、明らかな解決策は、上記の関係で FetchType.EAGER に変更することです。違う!これを行うと、session.merge を使用して所有側 (Compound) を更新できなくなります。これにより、StackOverFlowError が発生します。

実際の解決策は、実際にはすでに言及されていました。構成をそのままにして、必要なリレーションをサービス層に明示的にロードします。

@Transactional(readOnly = true)
@Override    
public Compound getById(Long id) {

    Compound  compound = compoundDAO.getById(id);
    for (CompoundComposition composition : compound.getComposition()){
        Hibernate.initialize(composition.getStructure());
    }        
    return compound;
}

時期尚早の最適化の罠に陥りがちであることは認めます。これはあまり効率的ではないように見えますし、そもそも SQL の仕組みを壊しているようにも見えます。しかし、幸運なことに、ほとんどの場合、CompoundComposition には 1 つまたは 2 つの要素しか含まれません。

于 2012-10-10T06:37:37.667 に答える
0

おそらくあなたは置くことができます

value.getComposition().get(i).getStructure();

メソッドの本体でgetById()、遅延読み込みがトランザクション内で発生するようにします。この場合、ループする必要があると思いますが、iこれは不便かもしれません。

于 2012-10-10T01:49:07.307 に答える