私の問題を示すために、私は簡単な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#colors
とCat#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#colors
とCat#nicknames
のセットを選択したいとします。実際のケースでは、複雑な構造を持つ1500個のエンティティをフェッチしますが、1つのフェッチされたエンティティが対応するResultSetの25.000行を生成して非常に長い解析時間が発生することは珍しくありません。
私の場合の遅延読み込みは、コードに混乱を招くため、私が使用したいオプションではありません。私が知る限り、遅れてロードされたCollectionは、最初の呼び出しで初期化されなければならず、これは私の実際のアプリケーションでは非常に大きな使い勝手の価格です。
cat
テーブルから1つ、cat_color
テーブルから1つ、cat_nickname
テーブルから1つ、3つの別個の選択を感謝します。
あなたがトレースしたリクエストは私にはうまく見えますが、それは私が "デカルト製品"と呼ぶものではありません。 1つの猫のデータを取得する他のSQLリクエストはどれですか?特定のユースケースで周辺機器のデータが必要ない場合は、(FetchType.LAZY)いくつかの結合を削除することができます。 – Tristan
私の場合の遅延読み込みは、コードに混乱を招くため、私が使用したいオプションではありません。私が知る限り、遅れてロードされたCollectionは、最初の呼び出しで初期化されなければならず、これは私の実際のアプリケーションでは非常に大きな使い勝手の価格です。 – Michal
他にどのようなSQLリクエストで単一の猫のデータを取得できますか? – Tristan