質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Spring

Spring Framework は、Javaプラットフォーム向けのオープンソースアプリケーションフレームワークです。 Java Platform上に、 Web ベースのアプリケーションを設計するための拡張機能が数多く用意されています。

Spring Boot

Spring Bootは、Javaのフレームワークの一つ。Springプロジェクトが提供する様々なフレームワークを統合した、アプリケーションを高速で開発するために設計されたフレームワークです。

Q&A

1回答

834閲覧

SpringDataJPAでの動的なfetchType変更の実現について

tak_n

総合スコア6

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Spring

Spring Framework は、Javaプラットフォーム向けのオープンソースアプリケーションフレームワークです。 Java Platform上に、 Web ベースのアプリケーションを設計するための拡張機能が数多く用意されています。

Spring Boot

Spring Bootは、Javaのフレームワークの一つ。Springプロジェクトが提供する様々なフレームワークを統合した、アプリケーションを高速で開発するために設計されたフレームワークです。

2グッド

1クリップ

投稿2018/04/24 12:00

前提・実現したいこと

補足情報の環境で、以下のようなリレーションシップを持つエンティティで、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

A-pZ, kochoru👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

この方法だとエンティティグラフを使用しながらSpringDataJPAの命名規則によるクエリの自動生成を使用することができません。

この問題に関しては以下のライブラリがあるので、使って見てマッチするか試してみると良いと思います。

https://github.com/Cosium/spring-data-jpa-entity-graph

投稿2018/05/15 03:36

YukiYoshida

総合スコア93

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問