«1. Обзор
Проще говоря, графы сущностей — это еще один способ описания запроса в JPA 2.1. Мы можем использовать их для формулирования более эффективных запросов.
В этом руководстве мы узнаем, как реализовать Entity Graphs с помощью Spring Data JPA на простом примере.
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
}
Теперь давайте определим сущность Characteristic:
@Entity
public class Characteristic {
@Id
private Long id;
private String type;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn
private Item item;
//Getters and Setters
}
Как видно из кода, как поле характеристик в объекте Item, так и поле элемента в объекте Characteristic загружаются лениво с использованием параметра выборки. Итак, наша цель здесь — жадно загружать их во время выполнения.
3. Графики сущностей
В Spring Data JPA мы можем определить граф сущностей, используя комбинацию аннотаций @NamedEntityGraph и @EntityGraph. Или мы также можем определить специальные графы сущностей, используя только аргумент attributePaths аннотации @EntityGraph.
Давайте посмотрим, как это можно сделать.
3.1. С помощью @NamedEntityGraph
Во-первых, мы можем использовать аннотацию JPA @NamedEntityGraph непосредственно к нашей сущности Item:
@Entity
@NamedEntityGraph(name = "Item.characteristics",
attributeNodes = @NamedAttributeNode("characteristics")
)
public class Item {
//...
}
А затем мы можем прикрепить аннотацию @EntityGraph к одному из методов нашего репозитория:
public interface ItemRepository extends JpaRepository<Item, Long> {
@EntityGraph(value = "Item.characteristics")
Item findByName(String name);
}
Как видно из кода, мы передали имя графа сущности, который мы создали ранее для сущности Item, в аннотацию @EntityGraph. Когда мы вызываем метод, этот запрос будет использовать Spring Data.
Значением по умолчанию аргумента типа аннотации @EntityGraph является EntityGraphType.FETCH. Когда мы используем это, модуль данных Spring применит стратегию FetchType.EAGER к указанным узлам атрибутов. А для остальных будет применяться стратегия FetchType.LAZY.
Таким образом, в нашем случае свойство характеристик будет загружено с готовностью, даже несмотря на то, что стратегия выборки по умолчанию аннотации @OneToMany является ленивой.
Одна загвоздка здесь в том, что если определенная стратегия выборки является EAGER, то мы не можем изменить ее поведение на LAZY. Это предусмотрено дизайном, поскольку последующим операциям могут понадобиться быстро извлеченные данные на более позднем этапе выполнения.
3.2. Без @NamedEntityGraph
Или мы также можем определить специальный граф сущностей с помощью attributePaths.
Давайте добавим специальный граф объекта в наш CharacteristicsRepository, который жадно загружает родительский элемент Item: это свойство.
public interface CharacteristicsRepository
extends JpaRepository<Characteristic, Long> {
@EntityGraph(attributePaths = {"item"})
Characteristic findByType(String type);
}
Это удобно, так как мы можем определить граф сущностей в строке вместо ссылки на существующий именованный граф сущностей.
4. Тестовый пример
Теперь, когда мы определили наши графы сущностей, давайте создадим тестовый пример для проверки:
Первый тест будет использовать граф сущностей, определенный с помощью аннотации @NamedEntityGraph.
@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);
}
}
Давайте посмотрим на SQL, сгенерированный Hibernate:
Для сравнения, давайте удалим аннотацию @EntityGraph из репозитория и проверим запрос:
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, не загружает какие-либо свойства объекта Characteristic. В результате загружается только объект Item.
select
item0_.id as id1_10_,
item0_.name as name2_10_
from
item item0_
where
item0_.name=?
Наконец, давайте сравним запросы Hibernate второго теста с аннотацией @EntityGraph:
И запрос без аннотации @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=?
5. Заключение
select
characteri0_.id as id1_4_,
characteri0_.item_id as item_id3_4_,
characteri0_.type as type2_4_
from
characteristic characteri0_
where
characteri0_.type=?
В этом руководстве , мы узнали, как использовать графы сущностей JPA в Spring Data. С помощью Spring Data мы можем создать несколько методов репозитория, которые связаны с разными графами сущностей.
Примеры для этой статьи доступны на GitHub.
«