前提・実現したいこと
補足情報の環境で、以下のようなリレーションシップを持つエンティティで、TestEntityParentに対してリポジトリTestEntityParentDaoを使用してDBへのクエリの発行を行っています。
その際に無駄なフェッチをなくしSQL効率を上げるために、発行するクエリによって各属性のfetch属性を動的に変更したいです。
発生している問題
調べたところ、エンティティグラフを見つけ、
2種類の定義方法をそれぞれ試みたところ下記のような問題点が浮上しました。
・アノテーションを使用した定義
アノテーションを使用した静的定義だとエンティティグラフの属性ノード・サブグラフの定義が固定化され、エンティティグラフでfetch方式をEAGERにした場合にSQLがまとまりleft outer joinで結合されるため、望んでいない結合結果になってしまい汎用性に欠けてしまいます。
また、findAll()時以外はTestEntityChildrenのfetch属性をLAZYにし、それ以外をEAGER、
findAll()時は全エンティティをEAGERといったような定義ができませんでした。
(fetch属性にfetchType.EAGERを明示的に指定した場合、エンティティグラフでLAZYに指定してもEAGERが優先されてしまうため)
・クエリ発行用メソッド内での独自定義
メソッド内でEntityManagerクラスのcreateEntityGraphメソッドを使用してエンティティグラフを生成し、クエリ発行用メソッドごとにエンティティグラフの定義を変更するように対応。
fetch属性をEAGERにした際に望んでいない結合結果になってしまう問題もJPQLでDISTINCTを使用することで回避。
実現は可能でしたが、クエリごとに定義しなければならないためコード量が膨大になってしまいます。
また、この方法だとエンティティグラフを使用しながらSpringDataJPAの命名規則によるクエリの自動生成を使用することができません。
こういった場合、どのように対応するべきなのでしょうか。
エンティティグラフ以外でも何か対応する方法がありましたらご教授いただけると幸いです。
初めての質問で分かりづらい箇所があるかと思いますが、よろしくお願いいたします。
エンティティ簡易関係図
ソースコード(変更前)
Java
1@Entity(name = "test_entity_parent") 2public class TestEntityParent { 3 4 @Id 5 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "test_entity_parent_id_seq") 6 @SequenceGenerator(name = "test_entity_parent_id_seq", sequenceName = "test_entity_parent_id_seq", allocationSize = 1) 7 @Column(columnDefinition = "default nextval('test_entity_parent_id_seq')") 8 private Integer id; 9 10 private String name; 11 12 //動的フェッチ対応前はfetch = FetchType.EAGERを指定 13 @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true) 14 @OrderBy(clause="id") 15 private Set<TestEntityChildren> childrens = new LinkedHashSet<>(); 16 17 //動的フェッチ対応前はfetch = FetchType.EAGERを指定 18 @OneToOne(mappedBy = "parent",cascade=CascadeType.ALL, orphanRemoval = true) 19 private TestEntityAnotherChildren testEntityAnotherChildren; 20 21 /* getter、setter省略 */ 22}
Java
1@Entity(name = "test_entity_children") 2public class TestEntityChildren { 3 4 @Id 5 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "test_entity_children_id_seq") 6 @SequenceGenerator(name = "test_entity_children_id_seq", sequenceName = "test_entity_children_id_seq", allocationSize = 1) 7 @Column(columnDefinition = "default nextval('test_entity_children_id_seq')") 8 private Integer id; 9 10 @ManyToOne 11 @JoinColumn(name = "p_id") 12 private TestEntityParent parent; 13 14 /* getter、setter省略 */ 15}
Java
1@Entity(name = "test_entity_anoterchildren") 2public class TestEntityAnotherChildren { 3 4 @Id 5 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "test_entity_anoterchildren_id_seq") 6 @SequenceGenerator(name = "test_entity_anoterchildren_id_seq", sequenceName = "test_entity_anoterchildren_id_seq", allocationSize = 1) 7 @Column(columnDefinition = "default nextval('test_entity_anoterchildren_id_seq')") 8 private Integer id; 9 10 @OneToOne 11 @JoinColumn(name = "p_id") 12 private TestEntityParent parent; 13 14 @ManyToOne 15 @JoinColumn(name = "ap_id") 16 private TestEntityAnotherParent a_parent; 17 18 //動的フェッチ対応前はfetch = FetchType.EAGERを指定 19 @OneToMany(mappedBy = "a_children", cascade = CascadeType.ALL, orphanRemoval = true) 20 private Set<TestEntityGrandSon> grandsons = new LinkedHashSet<>(); 21 22 /* getter、setter省略 */ 23}
Java
1@Entity(name = "test_entity_anoterparent") 2public class TestEntityAnotherParent { 3 4 @Id 5 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "test_entity_anoterparent_id_seq") 6 @SequenceGenerator(name = "test_entity_anoterparent_id_seq", sequenceName = "test_entity_anoterparent_id_seq", allocationSize = 1) 7 @Column(columnDefinition = "default nextval('test_entity_anoterparent_id_seq')") 8 private Integer id; 9 10 private String name; 11 12 //動的フェッチ対応前はfetch = FetchType.EAGERを指定 13 @OneToMany(mappedBy = "a_parent", cascade = CascadeType.ALL, orphanRemoval = true) 14 @OrderBy(clause="id") 15 private Set<TestEntityAnotherChildren> a_childrens = new LinkedHashSet<>(); 16 17 /* getter、setter省略 */ 18}
Java
1@Entity(name = "test_entity_grandson") 2public class TestEntityGrandSon { 3 4 @Id 5 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "test_entity_grandson_id_seq") 6 @SequenceGenerator(name = "test_entity_grandson_id_seq", sequenceName = "test_entity_grandson_id_seq", allocationSize = 1) 7 @Column(columnDefinition = "default nextval('test_entity_grandson_id_seq')") 8 private Integer id; 9 10 @ManyToOne 11 @JoinColumn(name = "ac_id") 12 private TestEntityAnotherChildren a_children; 13 14 /* getter、setter省略 */ 15}
Java
1public interface TestEntityParentDao extends JpaRepository<TestEntityParent,Integer> { 2 public List<TestEntityParent> findAll(); 3 public TestEntityParent findById(Integer id); 4}
アノテーションを使用した定義時変更個所
Java
1@NamedEntityGraph( 2 name="TestEntityParent.graph", 3 attributeNodes={ 4 @NamedAttributeNode(value="id") 5 ,@NamedAttributeNode(value="name") 6 ,@NamedAttributeNode(value="childrens", subgraph = "childrens") 7 ,@NamedAttributeNode(value="testEntityAnotherChildren", subgraph = "testEntityAnotherChildren") 8 }, 9 subgraphs = { 10 @NamedSubgraph(name = "testEntityAnotherChildren", attributeNodes = { 11 @NamedAttributeNode("id") 12 ,@NamedAttributeNode("parent") 13 ,@NamedAttributeNode(value="a_parent", subgraph = "a_parent") 14 ,@NamedAttributeNode(value="grandsons", subgraph = "grandsons") 15 }) 16 ,@NamedSubgraph(name = "a_parent", attributeNodes = { 17 @NamedAttributeNode("id") 18 ,@NamedAttributeNode("name") 19 ,@NamedAttributeNode("a_childrens") 20 }) 21 ,@NamedSubgraph(name = "grandsons", attributeNodes = { 22 @NamedAttributeNode("id") 23 ,@NamedAttributeNode("a_children") 24 }) 25 ,@NamedSubgraph(name = "childrens", attributeNodes = { 26 @NamedAttributeNode("id") 27 ,@NamedAttributeNode("parent") 28 }) 29 } 30) 31@Entity(name = "test_entity_parent") 32public class TestEntityParent { 33 34 @Id 35 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "test_entity_parent_id_seq") 36 @SequenceGenerator(name = "test_entity_parent_id_seq", sequenceName = "test_entity_parent_id_seq", allocationSize = 1) 37 @Column(columnDefinition = "default nextval('test_entity_parent_id_seq')") 38 private Integer id; 39 40 private String name; 41 42 //動的フェッチ対応前はfetch = FetchType.EAGERを指定 43 @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true) 44 @OrderBy(clause="id") 45 private Set<TestEntityChildren> childrens = new LinkedHashSet<>(); 46 47 //動的フェッチ対応前はfetch = FetchType.EAGERを指定 48 @OneToOne(mappedBy = "parent",cascade=CascadeType.ALL, orphanRemoval = true) 49 private TestEntityAnotherChildren testEntityAnotherChildren; 50 51 /* getter、setter省略 */ 52}
Java
1public interface TestEntityParentDao extends JpaRepository<TestEntityParent,Integer> { 2 @EntityGraph(value = "TestEntityParent.graph", type = EntityGraphType.LOAD) 3 public List<TestEntityParent> findAll(); 4 @EntityGraph(value = "TestEntityParent.graph", type = EntityGraphType.LOAD) 5 public TestEntityParent findById(Integer id); 6}
クエリ発行用メソッド内での独自定義時追加クラス
Java
1@Service 2@Transactional 3public class TestEntityParentService { 4 @Autowired 5 EntityManager em; 6 7 public List<TestEntityParent> findAll() { 8 EntityGraph<TestEntityParent> graph = em.createEntityGraph(TestEntityParent.class); 9 graph.addAttributeNodes("id"); 10 graph.addAttributeNodes("name"); 11 Subgraph<TestEntityChildren> childrenSubgraph = graph.addSubgraph("childrens"); 12 childrenSubgraph.addAttributeNodes("id"); 13 childrenSubgraph.addAttributeNodes("parent"); 14 Subgraph<TestEntityAnotherChildren> anotherChildrenSubgraph = graph.addSubgraph("testEntityAnotherChildren"); 15 anotherChildrenSubgraph.addAttributeNodes("id"); 16 anotherChildrenSubgraph.addAttributeNodes("parent"); 17 Subgraph<TestEntityAnotherParent> anoterParentSubgraph = anotherChildrenSubgraph.addSubgraph("a_parent"); 18 anoterParentSubgraph.addAttributeNodes("id"); 19 anoterParentSubgraph.addAttributeNodes("name"); 20 anoterParentSubgraph.addAttributeNodes("a_childrens"); 21 Subgraph<TestEntityGrandSon> grandsonSubgraph = anotherChildrenSubgraph.addSubgraph("grandsons"); 22 grandsonSubgraph.addAttributeNodes("id"); 23 grandsonSubgraph.addAttributeNodes("a_children"); 24 25 TypedQuery<TestEntityParent> query = em.createQuery( 26 "SELECT DISTINCT tep FROM test_entity_parent tep ORDER BY tep.id", 27 TestEntityParent.class); 28 query.setHint("javax.persistence.loadgraph", graph); 29 List<TestEntityParent> testEntityParentList = query.getResultList(); 30 return testEntityParentList; 31 } 32 33 public TestEntityParent findById(Integer id) { 34 EntityGraph<TestEntityParent> graph = em.createEntityGraph(TestEntityParent.class); 35 graph.addAttributeNodes("id"); 36 graph.addAttributeNodes("name"); 37 Subgraph<TestEntityAnotherChildren> anotherChildrenSubgraph = graph.addSubgraph("testEntityAnotherChildren"); 38 anotherChildrenSubgraph.addAttributeNodes("id"); 39 anotherChildrenSubgraph.addAttributeNodes("parent"); 40 Subgraph<TestEntityAnotherParent> anoterParentSubgraph = anotherChildrenSubgraph.addSubgraph("a_parent"); 41 anoterParentSubgraph.addAttributeNodes("id"); 42 anoterParentSubgraph.addAttributeNodes("name"); 43 anoterParentSubgraph.addAttributeNodes("a_childrens"); 44 Subgraph<TestEntityGrandSon> grandsonSubgraph = anotherChildrenSubgraph.addSubgraph("grandsons"); 45 grandsonSubgraph.addAttributeNodes("id"); 46 grandsonSubgraph.addAttributeNodes("a_children"); 47 48 TypedQuery<TestEntityParent> query = em.createQuery( 49 "SELECT tep FROM test_entity_parent tep WHERE tep.id = " + id, 50 TestEntityParent.class); 51 query.setHint("javax.persistence.loadgraph", graph); 52 TestEntityParent testEntityParent = query.getSingleResult(); 53 return testEntityParent; 54 } 55}
補足情報(FW/ツールのバージョンなど)
FW:Spring Boot v1.5.6
言語:Java 8
ORM:Spring Data JPA
DB:postgresql
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。