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

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

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

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

Spring

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

Spring Boot

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

受付中

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

tak_n
tak_n

総合スコア6

Java

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

Spring

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

Spring Boot

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

1回答

2評価

1クリップ

210閲覧

投稿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

@Entity(name = "test_entity_parent") public class TestEntityParent { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "test_entity_parent_id_seq") @SequenceGenerator(name = "test_entity_parent_id_seq", sequenceName = "test_entity_parent_id_seq", allocationSize = 1) @Column(columnDefinition = "default nextval('test_entity_parent_id_seq')") private Integer id; private String name; //動的フェッチ対応前はfetch = FetchType.EAGERを指定 @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true) @OrderBy(clause="id") private Set<TestEntityChildren> childrens = new LinkedHashSet<>(); //動的フェッチ対応前はfetch = FetchType.EAGERを指定 @OneToOne(mappedBy = "parent",cascade=CascadeType.ALL, orphanRemoval = true) private TestEntityAnotherChildren testEntityAnotherChildren; /* getter、setter省略 */ }

Java

@Entity(name = "test_entity_children") public class TestEntityChildren { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "test_entity_children_id_seq") @SequenceGenerator(name = "test_entity_children_id_seq", sequenceName = "test_entity_children_id_seq", allocationSize = 1) @Column(columnDefinition = "default nextval('test_entity_children_id_seq')") private Integer id; @ManyToOne @JoinColumn(name = "p_id") private TestEntityParent parent; /* getter、setter省略 */ }

Java

@Entity(name = "test_entity_anoterchildren") public class TestEntityAnotherChildren { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "test_entity_anoterchildren_id_seq") @SequenceGenerator(name = "test_entity_anoterchildren_id_seq", sequenceName = "test_entity_anoterchildren_id_seq", allocationSize = 1) @Column(columnDefinition = "default nextval('test_entity_anoterchildren_id_seq')") private Integer id; @OneToOne @JoinColumn(name = "p_id") private TestEntityParent parent; @ManyToOne @JoinColumn(name = "ap_id") private TestEntityAnotherParent a_parent; //動的フェッチ対応前はfetch = FetchType.EAGERを指定 @OneToMany(mappedBy = "a_children", cascade = CascadeType.ALL, orphanRemoval = true) private Set<TestEntityGrandSon> grandsons = new LinkedHashSet<>(); /* getter、setter省略 */ }

Java

@Entity(name = "test_entity_anoterparent") public class TestEntityAnotherParent { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "test_entity_anoterparent_id_seq") @SequenceGenerator(name = "test_entity_anoterparent_id_seq", sequenceName = "test_entity_anoterparent_id_seq", allocationSize = 1) @Column(columnDefinition = "default nextval('test_entity_anoterparent_id_seq')") private Integer id; private String name; //動的フェッチ対応前はfetch = FetchType.EAGERを指定 @OneToMany(mappedBy = "a_parent", cascade = CascadeType.ALL, orphanRemoval = true) @OrderBy(clause="id") private Set<TestEntityAnotherChildren> a_childrens = new LinkedHashSet<>(); /* getter、setter省略 */ }

Java

@Entity(name = "test_entity_grandson") public class TestEntityGrandSon { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "test_entity_grandson_id_seq") @SequenceGenerator(name = "test_entity_grandson_id_seq", sequenceName = "test_entity_grandson_id_seq", allocationSize = 1) @Column(columnDefinition = "default nextval('test_entity_grandson_id_seq')") private Integer id; @ManyToOne @JoinColumn(name = "ac_id") private TestEntityAnotherChildren a_children; /* getter、setter省略 */ }

Java

public interface TestEntityParentDao extends JpaRepository<TestEntityParent,Integer> { public List<TestEntityParent> findAll(); public TestEntityParent findById(Integer id); }

アノテーションを使用した定義時変更個所

Java

@NamedEntityGraph( name="TestEntityParent.graph", attributeNodes={ @NamedAttributeNode(value="id") ,@NamedAttributeNode(value="name") ,@NamedAttributeNode(value="childrens", subgraph = "childrens") ,@NamedAttributeNode(value="testEntityAnotherChildren", subgraph = "testEntityAnotherChildren") }, subgraphs = { @NamedSubgraph(name = "testEntityAnotherChildren", attributeNodes = { @NamedAttributeNode("id") ,@NamedAttributeNode("parent") ,@NamedAttributeNode(value="a_parent", subgraph = "a_parent") ,@NamedAttributeNode(value="grandsons", subgraph = "grandsons") }) ,@NamedSubgraph(name = "a_parent", attributeNodes = { @NamedAttributeNode("id") ,@NamedAttributeNode("name") ,@NamedAttributeNode("a_childrens") }) ,@NamedSubgraph(name = "grandsons", attributeNodes = { @NamedAttributeNode("id") ,@NamedAttributeNode("a_children") }) ,@NamedSubgraph(name = "childrens", attributeNodes = { @NamedAttributeNode("id") ,@NamedAttributeNode("parent") }) } ) @Entity(name = "test_entity_parent") public class TestEntityParent { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "test_entity_parent_id_seq") @SequenceGenerator(name = "test_entity_parent_id_seq", sequenceName = "test_entity_parent_id_seq", allocationSize = 1) @Column(columnDefinition = "default nextval('test_entity_parent_id_seq')") private Integer id; private String name; //動的フェッチ対応前はfetch = FetchType.EAGERを指定 @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true) @OrderBy(clause="id") private Set<TestEntityChildren> childrens = new LinkedHashSet<>(); //動的フェッチ対応前はfetch = FetchType.EAGERを指定 @OneToOne(mappedBy = "parent",cascade=CascadeType.ALL, orphanRemoval = true) private TestEntityAnotherChildren testEntityAnotherChildren; /* getter、setter省略 */ }

Java

public interface TestEntityParentDao extends JpaRepository<TestEntityParent,Integer> { @EntityGraph(value = "TestEntityParent.graph", type = EntityGraphType.LOAD) public List<TestEntityParent> findAll(); @EntityGraph(value = "TestEntityParent.graph", type = EntityGraphType.LOAD) public TestEntityParent findById(Integer id); }

クエリ発行用メソッド内での独自定義時追加クラス

Java

@Service @Transactional public class TestEntityParentService { @Autowired EntityManager em; public List<TestEntityParent> findAll() { EntityGraph<TestEntityParent> graph = em.createEntityGraph(TestEntityParent.class); graph.addAttributeNodes("id"); graph.addAttributeNodes("name"); Subgraph<TestEntityChildren> childrenSubgraph = graph.addSubgraph("childrens"); childrenSubgraph.addAttributeNodes("id"); childrenSubgraph.addAttributeNodes("parent"); Subgraph<TestEntityAnotherChildren> anotherChildrenSubgraph = graph.addSubgraph("testEntityAnotherChildren"); anotherChildrenSubgraph.addAttributeNodes("id"); anotherChildrenSubgraph.addAttributeNodes("parent"); Subgraph<TestEntityAnotherParent> anoterParentSubgraph = anotherChildrenSubgraph.addSubgraph("a_parent"); anoterParentSubgraph.addAttributeNodes("id"); anoterParentSubgraph.addAttributeNodes("name"); anoterParentSubgraph.addAttributeNodes("a_childrens"); Subgraph<TestEntityGrandSon> grandsonSubgraph = anotherChildrenSubgraph.addSubgraph("grandsons"); grandsonSubgraph.addAttributeNodes("id"); grandsonSubgraph.addAttributeNodes("a_children"); TypedQuery<TestEntityParent> query = em.createQuery( "SELECT DISTINCT tep FROM test_entity_parent tep ORDER BY tep.id", TestEntityParent.class); query.setHint("javax.persistence.loadgraph", graph); List<TestEntityParent> testEntityParentList = query.getResultList(); return testEntityParentList; } public TestEntityParent findById(Integer id) { EntityGraph<TestEntityParent> graph = em.createEntityGraph(TestEntityParent.class); graph.addAttributeNodes("id"); graph.addAttributeNodes("name"); Subgraph<TestEntityAnotherChildren> anotherChildrenSubgraph = graph.addSubgraph("testEntityAnotherChildren"); anotherChildrenSubgraph.addAttributeNodes("id"); anotherChildrenSubgraph.addAttributeNodes("parent"); Subgraph<TestEntityAnotherParent> anoterParentSubgraph = anotherChildrenSubgraph.addSubgraph("a_parent"); anoterParentSubgraph.addAttributeNodes("id"); anoterParentSubgraph.addAttributeNodes("name"); anoterParentSubgraph.addAttributeNodes("a_childrens"); Subgraph<TestEntityGrandSon> grandsonSubgraph = anotherChildrenSubgraph.addSubgraph("grandsons"); grandsonSubgraph.addAttributeNodes("id"); grandsonSubgraph.addAttributeNodes("a_children"); TypedQuery<TestEntityParent> query = em.createQuery( "SELECT tep FROM test_entity_parent tep WHERE tep.id = " + id, TestEntityParent.class); query.setHint("javax.persistence.loadgraph", graph); TestEntityParent testEntityParent = query.getSingleResult(); return testEntityParent; } }

補足情報(FW/ツールのバージョンなど)

FW:Spring Boot v1.5.6
言語:Java 8
ORM:Spring Data JPA
DB:postgresql

良い質問の評価を上げる

以下のような質問は評価を上げましょう

  • 質問内容が明確
  • 自分も答えを知りたい
  • 質問者以外のユーザにも役立つ

評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

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

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

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

teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

  • プログラミングに関係のない質問
  • やってほしいことだけを記載した丸投げの質問
  • 問題・課題が含まれていない質問
  • 意図的に内容が抹消された質問
  • 過去に投稿した質問と同じ内容の質問
  • 広告と受け取られるような投稿

評価を下げると、トップページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

まだ回答がついていません

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

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

ただいまの回答率
87.20%

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

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

質問する

関連した質問

同じタグがついた質問を見る

Java

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

Spring

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

Spring Boot

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