1. Обзор
Проще говоря, Entity Graphs — это еще один способ описать запрос в JPA 2.1. Мы можем использовать их для формулирования более эффективных запросов.
В этом руководстве мы узнаем, как реализовать графики сущностей с помощью 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
}
Теперь давайте определим объект
характеристики C :
@Entity
public class Characteristic {
@Id
private Long id;
private String type;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn
private Item item;
//Getters and Setters
}
Как мы видим в коде, как поле характеристик
в сущности Item , так и поле
элемента
в сущности Characteristic
загружаются лениво с использованием параметра fetch
. Итак, наша цель здесь — жадно загружать их во время выполнения.
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. Тестовый случай
Теперь, когда мы определили наши графы сущностей, давайте создадим тестовый пример, чтобы проверить это:
@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
, не загружает никаких свойств объекта Characteristic
. В результате загружается только объект 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 .