1. Overview
1.概述
Simply put, Entity Graphs are another way to describe a query in JPA 2.1. We can use them to formulate better-performing queries.
简单地说,Entity Graphs是JPA 2.1中描述查询的另一种方式。我们可以使用它们来制定性能更好的查询。
In this tutorial, we’re going to learn how to implement Entity Graphs with Spring Data JPA through a simple example.
在本教程中,我们将通过一个简单的例子来学习如何用Spring Data JPA实现实体图。
2. The Entities
2.实体
First, let’s create a model called Item which has multiple characteristics:
首先,让我们创建一个叫做Item的模型,它有多个特征。
@Entity
public class Item {
@Id
private Long id;
private String name;
@OneToMany(mappedBy = "item")
private List<Characteristic> characteristics = new ArrayList<>();
// getters and setters
}
Now let’s define the Characteristic entity:
现在让我们来定义C特性实体。
@Entity
public class Characteristic {
@Id
private Long id;
private String type;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn
private Item item;
//Getters and Setters
}
As we can see in the code, both the characteristics field in the Item entity and the item field in the Characteristic entity are loaded lazily using the fetch parameter. So, our goal here is to load them eagerly at runtime.
正如我们在代码中看到的,Item 实体中的characteristics字段和Characteristic实体中的item字段都是使用fetch参数懒散加载的。因此,我们在这里的目标是在运行时急切地加载它们。。
3. The Entity Graphs
3.实体图
In Spring Data JPA, we can define an entity graph using a combination of @NamedEntityGraph and @EntityGraph annotations. Or, we can also define ad-hoc entity graphs with just the attributePaths argument of the @EntityGraph annotation.
在Spring Data JPA中,我们可以使用@NamedEntityGraph和@EntityGraph注解的组合来定义一个实体图。或者,我们也可以只用@EntityGraph注解的attributePaths参数来定义特设的实体图。
Let’s see how it can be done.
让我们看看如何做到这一点。
3.1. With @NamedEntityGraph
3.1.使用@NamedEntityGraph
First, we can use JPA’s @NamedEntityGraph annotation directly on our Item entity:
首先,我们可以直接在我们的Item 实体上使用JPA的@NamedEntityGraph注解。
@Entity
@NamedEntityGraph(name = "Item.characteristics",
attributeNodes = @NamedAttributeNode("characteristics")
)
public class Item {
//...
}
And then, we can attach the @EntityGraph annotation to one of our repository methods:
然后,我们可以将@EntityGraph注解附加到我们的存储库方法之一。
public interface ItemRepository extends JpaRepository<Item, Long> {
@EntityGraph(value = "Item.characteristics")
Item findByName(String name);
}
As the code shows, we’ve passed the name of the entity graph, which we’ve created earlier on the Item entity, to the @EntityGraph annotation. When we call the method, that’s the query Spring Data will use.
如代码所示,我们将实体图的名称(我们之前在Item实体上创建的实体图)传递给@EntityGraph注释。当我们调用该方法时,Spring Data将使用该查询。
The default value of the type argument of the @EntityGraph annotation is EntityGraphType.FETCH. When we use this, the Spring Data module will apply the FetchType.EAGER strategy on the specified attribute nodes. And for others, the FetchType.LAZY strategy will be applied.
@EntityGraph注解的类型参数的默认值为EntityGraphType.FETCH。当我们使用它时,Spring Data模块将对指定的属性节点应用FetchType.EAGER策略。而对于其他的,将应用FetchType.LAZY策略。
So in our case, the characteristics property will be loaded eagerly, even though the default fetch strategy of the @OneToMany annotation is lazy.
所以在我们的例子中,characteristics属性将被急切地加载,尽管@OneToMany注解的默认获取策略是懒惰。
One catch here is that if the defined fetch strategy is EAGER, then we cannot change its behavior to LAZY. This is by design since the subsequent operations may need the eagerly fetched data at a later point during the execution.
这里的一个问题是,如果定义的fetch策略是EAGER,那么我们不能将其行为改为LAZY。这是在设计中,因为后续操作可能在执行的后期需要急于取走的数据。
3.2. Without @NamedEntityGraph
3.2.没有@NamedEntityGraph
Or, we can define an ad-hoc entity graph, too, with attributePaths.
或者,我们也可以用attributePaths定义一个临时的实体图。
Let’s add an ad-hoc entity graph to our CharacteristicsRepository that eagerly loads its Item parent:
让我们为我们的CharacteristicsRepository添加一个临时的实体图,急切地加载其Item父类。
public interface CharacteristicsRepository
extends JpaRepository<Characteristic, Long> {
@EntityGraph(attributePaths = {"item"})
Characteristic findByType(String type);
}
This will load the item property of the Characteristic entity eagerly, even though our entity declares a lazy-loading strategy for this property.
这将急切地加载item实体的Characteristic属性,尽管我们的实体为这个属性声明了一个懒加载策略。
This is handy since we can define the entity graph inline instead of referring to an existing named entity graph.
这很方便,因为我们可以在线定义实体图,而不是引用一个现有的命名实体图。
4. Test Case
4.测试案例
Now that we’ve defined our entity graphs let’s create a test case to verify it:
现在我们已经定义了我们的实体图,让我们创建一个测试案例来验证它。
@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);
}
}
The first test will use the entity graph defined using the @NamedEntityGraph annotation.
第一个测试将使用用@NamedEntityGraph注解定义的实体图。
Let’s see the SQL generated by Hibernate:
让我们看看由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=?
For comparison, let’s remove the @EntityGraph annotation from the repository and inspect the query:
为了比较,让我们从资源库中删除@EntityGraph注解,并检查查询。
select
item0_.id as id1_10_,
item0_.name as name2_10_
from
item item0_
where
item0_.name=?
From these queries, we can clearly observe that the query generated without @EntityGraph annotation is not loading any properties of Characteristic entity. As a result, it loads only the Item entity.
从这些查询中,我们可以清楚地观察到,没有@EntityGraph注解的查询没有加载Characteristic实体的任何属性。
Lastly, let’s compare the Hibernate queries of the second test with the @EntityGraph annotation:
最后,让我们比较一下第二个测试的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=?
And the query without the @EntityGraph annotation:
以及没有@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. Conclusion
5.结论
In this tutorial, we’ve learned how to use JPA Entity Graphs in Spring Data. With Spring Data, we can create multiple repository methods that are linked to different entity graphs.
在本教程中,我们已经学习了如何在Spring Data中使用JPA实体图。通过Spring Data,我们可以创建多个与不同实体图相关的存储库方法。
The examples for this article are available over on GitHub.
本文的例子可以在GitHub上找到,。