1. 概要

簡単に言えば、エンティティグラフはJPA 2.1でクエリを記述する別の方法です。これらを使用して、よりパフォーマンスの高いクエリを作成できます。

このチュートリアルでは、簡単な例を使用して、 Spring DataJPAを使用してエンティティグラフを実装する方法を学習します。

2. エンティティ

まず、複数の特性を持つItemというモデルを作成しましょう。

@Entity
public class Item {

    @Id
    private Long id;
    private String name;
    
    @OneToMany(mappedBy = "item")
    private List<Characteristic> characteristics = new ArrayList<>();

    // getters and setters
}

次に、C haracteristicエンティティを定義しましょう。

@Entity
public class Characteristic {

    @Id
    private Long id;
    private String type;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn
    private Item item;

    //Getters and Setters
}

コードからわかるように、 ItementityのcharacteristicsフィールドとCharacteristicエンティティのitemフィールドの両方が読み込まれます。 fetchパラメーターを怠惰に使用します。 したがって、ここでの目標は、実行時にそれらを熱心にロードすることです。

3. エンティティグラフ

Spring Data JPAでは、@NamedEntityGraphと@EntityGraphアノテーションの組み合わせを使用してエンティティグラフを定義できます。 または、@EntityGraphアノテーションのattributePaths引数のみを使用してアドホックエンティティグラフを定義することもできます。

それがどのように行われるか見てみましょう。

3.1. @NamedEntityGraphを使用

まず、JPAの @NamedEntityGraphアノテーションをアイテムエンティティに直接使用できます。

@Entity
@NamedEntityGraph(name = "Item.characteristics",
    attributeNodes = @NamedAttributeNode("characteristics")
)
public class Item {
	//...
}

次に、@EntityGraphアノテーションをリポジトリメソッドの1つにアタッチできます。

public interface ItemRepository extends JpaRepository<Item, Long> {

    @EntityGraph(value = "Item.characteristics")
    Item findByName(String name);
}

コードが示すように、以前にItemエンティティで作成したエンティティグラフの名前@EntityGraphアノテーションに渡しました。 メソッドを呼び出すとき、それはSpringDataが使用するクエリです。

@EntityGraphアノテーションのtype引数のデフォルト値はEntityGraphType.FETCHです。 これを使用すると、SpringDataモジュールは指定された属性ノードにFetchType.EAGER戦略を適用します。 また、その他の場合は、FetchType.LAZY戦略が適用されます。

したがって、この場合、 @OneToMany アノテーションのデフォルトのフェッチ戦略は怠惰ですが、characteristicsプロパティは熱心にロードされます。

ここでの1つの落とし穴は、定義されたフェッチ戦略がEAGERの場合、その動作を LAZYに変更できないことです。これは、後続の操作で熱心にフェッチされたデータが必要になる可能性があるため、仕様によるものです。実行中の後のポイント。

3.2. @NamedEntityGraphなし

または、attributePaths。を使用してアドホックエンティティグラフを定義することもできます。

Item親を熱心にロードするCharacteristicsRepositoryにアドホックエンティティグラフを追加しましょう。

public interface CharacteristicsRepository 
  extends JpaRepository<Characteristic, Long> {
    
    @EntityGraph(attributePaths = {"item"})
    Characteristic findByType(String type);    
}

これにより、Characteristicエンティティのitemプロパティが熱心に読み込まれますが、エンティティは、このプロパティの遅延読み込み戦略を宣言しています。

これは、既存の名前付きエンティティグラフを参照する代わりに、エンティティグラフをインラインで定義できるので便利です。

4. テストケース

エンティティグラフを定義したので、それを検証するためのテストケースを作成しましょう。

@DataJpaTest
@RunWith(SpringRunner.class)
@Sql(scripts = "/entitygraph-data.sql")
public class EntityGraphIntegrationTest {
   
    @Autowired
    private ItemRepository itemRepo;
    
    @Autowired
    private CharacteristicsRepository characteristicsRepo;
    
    @Test
    public void givenEntityGraph_whenCalled_shouldRetrunDefinedFields() {
        Item item = itemRepo.findByName("Table");
        assertThat(item.getId()).isEqualTo(1L);
    }
    
    @Test
    public void givenAdhocEntityGraph_whenCalled_shouldRetrunDefinedFields() {
        Characteristic characteristic = characteristicsRepo.findByType("Rigid");
        assertThat(characteristic.getId()).isEqualTo(1L);
    }
}

最初のテストでは、@NamedEntityGraphアノテーションを使用して定義されたエンティティグラフを使用します。

Hibernateによって生成されたSQLを見てみましょう。

select 
    item0_.id as id1_10_0_,
    characteri1_.id as id1_4_1_,
    item0_.name as name2_10_0_,
    characteri1_.item_id as item_id3_4_1_,
    characteri1_.type as type2_4_1_,
    characteri1_.item_id as item_id3_4_0__,
    characteri1_.id as id1_4_0__
from 
    item item0_ 
left outer join 
    characteristic characteri1_ 
on 
    item0_.id=characteri1_.item_id 
where 
    item0_.name=?

比較のために、リポジトリから @EntityGraph アノテーションを削除し、クエリを調べてみましょう。

select 
    item0_.id as id1_10_,
    item0_.name as name2_10_ 
from 
    item item0_ 
where 
    item0_.name=?

これらのクエリから、 @EntityGraph アノテーションなしで生成されたクエリは、特性エンティティのプロパティをロードしていないことがはっきりとわかります。その結果、 Item[[ X213X]エンティティ。

最後に、2番目のテストのHibernateクエリを@EntityGraphアノテーションと比較してみましょう。

select 
    characteri0_.id as id1_4_0_,
    item1_.id as id1_10_1_,
    characteri0_.item_id as item_id3_4_0_,
    characteri0_.type as type2_4_0_,
    item1_.name as name2_10_1_ 
from 
    characteristic characteri0_ 
left outer join 
    item item1_ 
on 
    characteri0_.item_id=item1_.id 
where 
    characteri0_.type=?

そして、 @EntityGraph アノテーションのないクエリ:

select 
    characteri0_.id as id1_4_,
    characteri0_.item_id as item_id3_4_,
    characteri0_.type as type2_4_ 
from 
    characteristic characteri0_ 
where 
    characteri0_.type=?

5. 結論

このチュートリアルでは、SpringDataでJPAエンティティグラフを使用する方法を学習しました。 Spring Dataを使用すると、さまざまなエンティティグラフにリンクされた複数のリポジトリメソッドを作成できます

この記事の例は、GitHubから入手できます。