Рубрики
Без рубрики

Spring Data JPA и именованные графы сущностей

Узнайте, как лучше управлять сущностями, которые вы хотите извлечь, используя Spring JPA.

Автор оригинала: baeldung.

1. Обзор

Проще говоря, Entity Graphs -это еще один способ описания запроса в JPA 2.1. Мы можем использовать их для формулирования более эффективных запросов.

В этом уроке мы узнаем, как реализовать графики сущностей с помощью Spring Data JPA на простом примере.

2. Сущности

Во-первых, давайте создадим модель под названием Элемент который имеет несколько характеристик:

@Entity
public class Item {

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

    // getters and setters
}

Теперь давайте определим сущность C characteristic :

@Entity
public class Characteristic {

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

    //Getters and Setters
}

Как мы видим в коде, как поле characteristics в сущности Item , так и поле item в сущности Characteristic загружаются лениво с помощью параметра fetch . Итак, наша цель здесь-охотно загружать их во время выполнения.

3. Графы Сущностей

В Spring Data JPA мы можем определить граф сущностей, используя комбинацию @NamedEntityGraph и @EntityGraph аннотаций . Или мы также можем определить специальные графы сущностей только с помощью аргумента attribute Paths аннотации @EntityGraph .

Давайте посмотрим, как это можно сделать.

3.1. С помощью @NamedEntityGraph

Во-первых, мы можем использовать аннотацию JPA @NamedEntityGraph непосредственно на нашем Item entity:

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

А затем мы можем прикрепить аннотацию @EntityGraph к одному из наших методов репозитория:

public interface ItemRepository extends JpaRepository {

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

Как показывает код, мы передали имя графа сущностей, который мы создали ранее на Item entity, аннотации @EntityGraph . Когда мы вызываем метод, это строка запроса, которую будут использовать данные.

Значение по умолчанию аргумента type аннотации @EntityGraph равно EntityGraphType.FETCH . Когда мы используем это, модуль Spring Data будет применять тип FetchType.EAGER стратегия на указанных атрибутивных узлах. А для других-тип FetchType.Будет применена стратегия LAZY .

Таким образом, в нашем случае свойство characteristics будет загружаться с нетерпением, даже если стратегия выборки по умолчанию аннотации @OneToMany является ленивой.

Одна загвоздка здесь заключается в том, что если определенная стратегия fetch является НЕТЕРПЕЛИВОЙ, то мы не можем изменить ее поведение на ЛЕНИВОЕ . Это сделано специально, поскольку последующие операции могут нуждаться в нетерпеливо извлеченных данных на более позднем этапе выполнения.

3.2. Без @NamedEntityGraph

Или мы можем определить специальный граф сущностей to, width attribute Paths.

Давайте добавим специальный граф сущностей в наш репозиторий Характеристик , который охотно загружает свой Элемент родитель:

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

Это будет загружать свойство item объекта Characteristic с нетерпением, даже если наша сущность объявляет стратегию ленивой загрузки для этого свойства.

Это удобно, так как мы можем определить граф сущностей встроенным, а не ссылаться на существующий именованный граф сущностей.

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 .

Давайте посмотрим на SQL, сгенерированный Hibernate:

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 annotation , не загружает никаких свойств Characteristic entity. В результате он загружает только объект Item .

Наконец, давайте сравним запросы 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. Заключение

В этом уроке мы узнали, как использовать графики сущностей JPA в Spring Data. С помощью Spring Data мы можем создать несколько методов репозитория, связанных с различными графами сущностей .

Примеры для этой статьи доступны на GitHub .