2017-04-18 7 views
2

私の問題を示すために、私は簡単なSpringブートアプリケーションを作成しました。 1つのエンティティ(IDを持ち、2つのStringプロパティと2つのSets<String>セット)を持っています。Hibernateエンティティを読み込むときにカーステーション製品を最適化する方法

package com.mk.cat.domain; 

import javax.persistence.*; 
import java.util.Set; 

@Entity 
@Table(name = "cat") 
public class Cat { 
    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    @Column(name = "id") 
    private Long id; 

    @Column(name = "name") 
    private String name; 

    @Column(name = "sex") 
    private String sex; 

    @ElementCollection(fetch = FetchType.EAGER) 
    @Column(name = "color") 
    @CollectionTable(
      name = "cat_color", 
      joinColumns = @JoinColumn(name = "cat_id")) 
    private Set<String> colors; 

    @ElementCollection(fetch = FetchType.EAGER) 
    @Column(name = "nickname") 
    @CollectionTable(
      name = "cat_nickname", 
      joinColumns = @JoinColumn(name = "cat_id")) 
    private Set<String> nicknames; 

    public Long getId() { 
     return id; 
    } 

    public void setId(Long id) { 
     this.id = id; 
    } 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

    public String getSex() { 
     return sex; 
    } 

    public void setSex(String sex) { 
     this.sex = sex; 
    } 

    public Set<String> getColors() { 
     return colors; 
    } 

    public void setColors(Set<String> colors) { 
     this.colors = colors; 
    } 

    public Set<String> getNicknames() { 
     return nicknames; 
    } 

    public void setNicknames(Set<String> nicknames) { 
     this.nicknames = nicknames; 
    } 
} 

持続し、DBから猫のエンティティをロードする簡単なコードもあります。

package com.mk.cat; 

import com.google.common.collect.Sets; 
import com.mk.cat.domain.Cat; 
import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.boot.CommandLineRunner; 
import org.springframework.boot.SpringApplication; 
import org.springframework.boot.autoconfigure.SpringBootApplication; 

@SpringBootApplication 
public class CatApplication implements CommandLineRunner { 

    private final CatRepository catRepository; 

    private static final Logger LOGGER = LoggerFactory.getLogger(CatApplication.class); 

    @Autowired 
    public CatApplication(CatRepository catRepository) { 
     this.catRepository = catRepository; 
    } 

    public static void main(String[] args) { 
     SpringApplication.run(CatApplication.class, args); 
    } 

    @Override 
    public void run(String... args) throws Exception { 
     Cat cat = new Cat(); 
     cat.setName("Ben"); 
     cat.setSex("Male"); 
     cat.setNicknames(Sets.newHashSet("Fluffy", "Mr. Tomcat", "Catburger")); 
     cat.setColors(Sets.newHashSet("Black", "White")); 

     final Cat saved = catRepository.save(cat); 
     LOGGER.info("Cat saved={}", cat); 

     catRepository.findOne(saved.getId()); 
    } 
} 

私は、Hibernateをトレースして、私はCatは、このSQLでDBからロードされていることを発見しました。

select cat0_.id as id1_0_0_, 
    cat0_.name as name2_0_0_, 
    cat0_.sex as sex3_0_0_, 
    colors1_.cat_id as cat_id1_1_1_, 
    colors1_.color as color2_1_1_, 
    nicknames2_.cat_id as cat_id1_2_2_, 
    nicknames2_.nickname as nickname2_2_2_ 
    from cat cat0_ 
    left outer join cat_color colors1_ on cat0_.id=colors1_.cat_id 
    left outer join cat_nickname nicknames2_ on cat0_.id=nicknames2_.cat_id 
where cat0_.id=1 

Hibernateは次にCat#colorsCat#nicknamesセットを表すcatテーブルと二つのテーブルの行からこのデカルト積を得ます。

id1_0_0_ name2_0_0_ sex3_0_0_ cat_id1_1_1_ color2_1_1_ cat_id1_2_2_ nickname2_2_2_ 
1 Ben Male 1 Black 1 Fluffy 
1 Ben Male 1 Black 1 Catburger 
1 Ben Male 1 Black 1 Mr. Tomcat 
1 Ben Male 1 White 1 Fluffy 
1 Ben Male 1 White 1 Catburger 
1 Ben Male 1 White 1 Mr. Tomcat 

Hibernateは、各行を通り、ResultSetのすべての項目を解析してエンティティを作成します。このアプローチを最適化することは何とか可能ですか?重大なパフォーマンス上の問題により、副選択でCat#colorsCat#nicknamesのセットを選択したいとします。実際のケースでは、複雑な構造を持つ1500個のエンティティをフェッチしますが、1つのフェッチされたエンティティが対応するResultSetの25.000行を生成して非常に長い解析時間が発生することは珍しくありません。

私の場合の遅延読み込みは、コードに混乱を招くため、私が使用したいオプションではありません。私が知る限り、遅れてロードされたCollectionは、最初の呼び出しで初期化されなければならず、これは私の実際のアプリケーションでは非常に大きな使い勝手の価格です。

catテーブルから1つ、cat_colorテーブルから1つ、cat_nicknameテーブルから1つ、3つの別個の選択を感謝します。

+1

あなたがトレースしたリクエストは私にはうまく見えますが、それは私が "デカルト製品"と呼ぶものではありません。 1つの猫のデータを取得する他のSQLリクエストはどれですか?特定のユースケースで周辺機器のデータが必要ない場合は、(FetchType.LAZY)いくつかの結合を削除することができます。 – Tristan

+0

私の場合の遅延読み込みは、コードに混乱を招くため、私が使用したいオプションではありません。私が知る限り、遅れてロードされたCollectionは、最初の呼び出しで初期化されなければならず、これは私の実際のアプリケーションでは非常に大きな使い勝手の価格です。 – Michal

+0

他にどのようなSQLリクエストで単一の猫のデータを取得できますか? – Tristan

答えて

0

私はHibernateの解決策を見つけました。これは、Hibernateに参加の代わりに別の選択肢を選択させるために、@Fetch(FetchMode.SELECT)が仕事をしました。

@Fetch(FetchMode.SELECT) 
@ElementCollection(fetch = FetchType.EAGER) 
@Column(name = "nickname") 
@CollectionTable(
     name = "cat_nickname", 
     joinColumns = @JoinColumn(name = "cat_id")) 
private Set<String> nicknames; 
関連する問題