JPA效率优化(EntityGraph)

JPA效率优化
n+1问题
当我们使用JPA提供给我们的find方法时,如果查询出来的对象关联着另外10个对象,那么JPA将会发送1+10次查询(这个对象本身要查询一次,然后每个关联对象再查询一次)。此时如果有100个用户正同时发送请求,这个时候就会产生1100次查询,这将会大大增加服务器的开销,因此我们要避免这个问题。
Fetch Join
在JPA中,我们可以使用fetch join来获取我们需要加载的关联实体。
优点:它只需要一次查询就能够获取到我们需要的所有关联实体。
缺点:不够灵活,选择不同的关联实体就要重写JPQL语句或者是用Criteria API来封装一个很好的接口,当一个对象有很多关联实体的时候,要重写很多的JPQL语句。但如果在实际开发中,关联实体的组合数量较少的情况下是可以用的。
NameEntityGraph
这是JPA2.1的新特性,它的主要作用是来设置懒加载需要加载的关联对象,并且支持多层关联,比如account.getRoles().get(0).getPages(),来获取一个账号的某个角色所能访问的页面。
下面来看一个例子,假如有如下3个类
Account

@Entity
@Table(name = ACCOUNT)
@NamedEntityGraphs({
        @NamedEntityGraph(name = "account.all", 
                attributeNodes = {//attributeNodes 来定义需要懒加载的属性
                @NamedAttributeNode("student"), @NamedAttributeNode("staff"),//无延伸
                @NamedAttributeNode(value = "roles",//要懒加载roles属性中的pages元素
                        subgraph = "pages"),
                },
                subgraphs = {//subgraphs 来定义关联对象的属性
                        @NamedSubgraph(name = "pages",//一层延伸
                                attributeNodes = @NamedAttributeNode("pages")),
                        @NamedSubgraph(name = "role",//两层延伸
                                attributeNodes = @NamedAttributeNode(
                                        value = "role",
                                        subgraph = "pages")) }) })
public class Account extends EntityId {

@OneToOne
@JoinColumn(name = ACCOUNT_STAFF)
private Staff staff;

@OneToOne
@JoinColumn(name = ACCOUNT_STUDENT)
private Student student;

@Column
private String username;

@Column
private String password;

@ManyToMany(cascade = CascadeType.REFRESH)
@JoinTable(name = REL_ACCOUNT_ROLE,
        joinColumns = { @JoinColumn(name = "accounts",
                referencedColumnName = "id") },
        inverseJoinColumns = {
                @JoinColumn(name = "roles", referencedColumnName = "id") })
@OrderBy(value = "id DESC")
private Set<Role> roles = new HashSet<>();

@OneToMany(mappedBy = "account")
private Set<AccountPrivilege> accountPrivileges = new HashSet<>();

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

Role

@Entity
@Table(name = ROLE,
uniqueConstraints = @UniqueConstraint(columnNames = { “name” }))
public class Role extends EntityId {

@Column
private String name;

@Enumerated
private DomainLevel domainLevel;

@ManyToMany
@JoinTable(name = REL_ACCOUNT_ROLE,
        joinColumns = {
                @JoinColumn(name = "roles", referencedColumnName = "id") },
        inverseJoinColumns = { @JoinColumn(name = "accounts",
                referencedColumnName = "id") })
private Set<Account> accounts = new HashSet<>();

@ManyToMany
private Set<Page> pages = new HashSet<>();

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

Page

@Entity
@Table(name = PAGE_PRIVILEGE)
public class Page {

@Id
long id;

private String pageName;

private String url;

private String iconClass;

private String discription = "";

@ManyToMany(mappedBy = "pages")
private Set<Role> roles = new HashSet<>();

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
下来我要根据username查询Account,并且需要获取该账号的某个角色能访问的页面,即account.getRoles().get(0).getPages()

AccountRepository
@Transactional(readOnly = true)
@RepositoryRestResource(exported = false)
public interface AccountRepository extends JpaRepository {

//通过@EntityGraph来指定Account类中定义的NamedEntityGraph
@EntityGraph(value="account.all",type=EntityGraphType.FETCH)
public Account findOneByUsername(String username);

}
1
2
3
4
5
6
7
8
9

调用findOneByUsername即能够查询改Account,并且会同时查询出student、staff、roles、roles.pages。查看控制台的话会发现就生成一条sql语句,即只查询一次。

作者:大浪中航行
来源:CSDN
原文:https://blog.csdn.net/dalangzhonghangxing/article/details/56680629
版权声明:本文为博主原创文章,转载请附上博文链接!

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享