web-dev-qa-db-ja.com

Hibernate CollectionOfElements EAGERフェッチで重複する要素

CollectionOfElementsとしてマップされた値のコレクションを持つSynonymMappingというクラスがあります

@Entity(name = "synonymmapping")
public class SynonymMapping {

    @Id private String keyId;

    //@CollectionOfElements(fetch = FetchType.EAGER)
    @CollectionOfElements
    @JoinTable(name="synonymmappingvalues", joinColumns={@JoinColumn(name="keyId")})
    @Column(name="value", nullable=false)
    @Sort(type=SortType.NATURAL)
    private SortedSet<String> values;

    public SynonymMapping() {
        values = new TreeSet<String>();
    }

    public SynonymMapping(String key, SortedSet<String> values) {
        this();
        this.keyId = key;
        this.values = values;
    }

    public String getKeyId() {
        return keyId;
    }

    public Set<String> getValues() {
        return values;
    }
}

2つのSynonymMappingオブジェクトをデータベースに保存し、保存したすべてのSynonymMappingオブジェクトを返すようにデータベースに要求し、保存した2つのオブジェクトを受信することを期待するテストがあります。

値のマッピングを変更して(コードのコメント化された行でコードに示されているように)熱心にテストを再実行すると、4つの一致が表示されます。

実行と実行の間にデータベースをクリアしました。この問題は、熱心なものと怠惰なものとの間で交換できます。

Hibernateがその下に作成する結合に関係していると思いますが、オンラインで明確な答えを見つけることができません。

熱心なフェッチがオブジェクトを複製している理由を誰かに教えてもらえますか?

ありがとう。

41
Rachel

一般に、マッピングで熱心なフェッチを強制することはお勧めしません-適切なクエリで熱心な結合を指定することをお勧めします(すべての状況でオブジェクトが意味をなさない/そのコレクションなしで有効であることを100%確信している場合を除く)投入されます)。

重複が発生する理由は、Hibernateがルートテーブルとコレクションテーブルを内部的に結合するためです。それらは実際には重複していることに注意してください。それぞれが3つのコレクション要素を持つ2つのSynonymMappingsの場合、6つの結果(2x3)、各SynonymMappingエンティティの3つのコピーが得られます。したがって、最も簡単な回避策は、結果をセットにラップして、結果が一意であることを確認することです。

29
ChssPly76

私は同じ問題に踏み込みました。@ CollectionOfElementsにFetchType.EAGERを設定すると、Hibernateはすべてを一度に取得しようとします。つまり、「マスター」オブジェクトにリンクされた要素のエントリごとに1つのクエリを使用します。コレクションに@Fetch(FetchMode.SELECT)アノテーションを追加すると、この問題はN + 1クエリのコストで正常に解決できます。私の場合、メタデータアイテム(ビデオコーデック、オーディオコーデック、サイズなど)のコレクションを持つMediaObjectエンティティが必要でした。 metadataItemsコレクションのマッピングは次のようになります。

 
 @ CollectionOfElements(targetElement = String.class、fetch = FetchType.EAGER)
 @ JoinTable(name = "mo_metadata_item"、joinColumns = @JoinColumn(name = "media_object_id") )
 @ MapKey(columns = @Column(name = "name"))
 @ Column(name = "value")
 @ Fetch(FetchMode.SELECT)
 private Map <String、String> metadataItems = new HashMap <String、String>(); 
69
user176668

私はこの問題に直面し、私はそれを使って解決しました

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

これにより、子テーブルに対して行われた結合によって発生する重複が解消されます。

7
Raja Anbazhagan

SELECT DISTINCT(Hibernate Query Language)句を次のように使用できます

SELECT DISTINCT synonym FROM SynonymMapping synonym LEFT JOIN FETCH synonym.values

DISTINCT句は、Hibernateの重複した参照を削除します。

コンポーネントと値タイプのコレクションはどちらも、そのライフサイクルが所有エンティティクラスにバインドされていますが、それらを取得するには、select句で宣言する必要があります。 (LEFT JOIN FETCH synonym.values)

ChssPly76の答えは別のアプローチですが、Setのセマンティックに従ってオーバーライドの等しいとハッシュコードメソッドを忘れないでください

よろしく、

2
Arthur Ronald

N + 1クエリで_FetchMode.SELECT_を使用する代わりに、BatchSize e.q. @BatchSize(size = 200)

DISTINCTおよび_Criteria.DISTINCT_ROOT_ENTITY_は、複数の関連付けを取得する必要がある場合には役に立ちません。この場合は、他のソリューションを参照してください: https://stackoverflow.com/a/46013654/54847

0
Grigory Kislin

私は単に追加することでそれを達成しました

session.createCriteria(ModelClass.class).setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

これは重複を取り除くのに役立ちます。

0
Rahul Gangahar