Skip to content

GitLab

  • Menu
Projects Groups Snippets
    • Loading...
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Sign in
  • S Spring 开发技术
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
  • Issues 0
    • Issues 0
    • List
    • Boards
    • Service Desk
    • Milestones
  • Merge requests 0
    • Merge requests 0
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Monitor
    • Monitor
    • Incidents
  • Packages & Registries
    • Packages & Registries
    • Infrastructure Registry
  • Analytics
    • Analytics
    • CI/CD
    • Repository
    • Value stream
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • wiki
  • Spring 开发技术
  • Wiki
  • 优化Spring Data JPA关联数据读取效率

优化Spring Data JPA关联数据读取效率 · Changes

Page history
创建优化Spring Data JPA关联数据读取效率 authored Aug 14, 2019 by 赵远's avatar 赵远
Hide whitespace changes
Inline Side-by-side
Showing with 152 additions and 0 deletions
+152 -0
  • 优化Spring-Data-JPA关联数据读取效率.md 优化Spring-Data-JPA关联数据读取效率.md +152 -0
  • No files found.
优化Spring-Data-JPA关联数据读取效率.md 0 → 100644
View page @ 37016c92
### 优化Spring Data JPA关联数据读取效率
在使用Spring Data JPA经常会使用 ManyToOne、OneToMany、ManyToMany、OneToOne作为实体之间的关联,如果关联使用不当,很容易造成很严重的性能问题,下面会讲解不同场景下如何优化关联关系提升数据的读取效率。
#### OneToMany/ManyToMany 优化
OneToMany 默认会使用延迟加载策略加载 ToMany 数据,除非必须使用ToMany数据,否则必须使用延迟加载策略。
1. 默认情况使用延迟加载
```java
public class Foo implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name")
private String name;
@Column(name = "code")
private String code;
@OneToMany(mappedBy = "foo")
private Set<Bar> bars;
}
```
2. 如果必须在读取列表的时候加载ToMany数据,使用JPA的EntityGraph注解,禁止使用默认的抓取策略,会导致 1+N次查询问题,造成严重的性能损耗。
```java
public interface BarRepository extends EntityGraphJpaRepository<Bar, Long> {
@Override
@EntityGraph(attributePaths = "foo")
Page<Bar> findAll(Pageable pageable);
}
```
#### ManyToOne/OneToOne 优化
ManyToOne默认不使用延迟加载,会以join的形式直接获取主表的数据,实际使用中需要关注一下几点:
1. 推荐子表关联主表的主键字段,否则会造成1+N次查询,影响性能。
2. 如果因为设计无法实现关联主表的主键子段,且每次读取列表都需要关联带出主表数据的情况,推荐使用EntityGraph注解。
3. 如果子表关联的不是主表的主键,且每次读取列表都无需带出主表信息,主表的entity只是为了数据过滤的话,推荐使用PersistentAttributeInterceptable手动管理entity的延迟加载属性。
以下是PersistentAttributeInterceptable的用法,ManyToOne的抓取策略设置成 LAZY,并将optional设置成false,添加 LazyToOne注解,将值设置成 NO_PROXY,即不走代理,使用自定义的拦截器处理延迟加载。
```java
public class Bar implements PersistentAttributeInterceptable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name")
private String name;
@Column(name = "foo_code")
private String fooCode;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "foo_code", referencedColumnName = "code", updatable = false, insertable = false, unique = true)
@LazyToOne(LazyToOneOption.NO_PROXY)
private Foo foo;
@Transient
private PersistentAttributeInterceptor interceptor;
@Override
public PersistentAttributeInterceptor $$_hibernate_getInterceptor() {
return this.interceptor;
}
@Override
public void $$_hibernate_setInterceptor(PersistentAttributeInterceptor interceptor) {
this.interceptor = interceptor;
}
public Foo getFoo() {
if (interceptor != null) {
return (Foo)interceptor.readObject(this, "foo", this.foo);
}
return foo;
}
public void setFoo(Foo foo) {
if (interceptor != null) {
this.foo = (Foo) interceptor.writeObject(this, "foo", this.foo, foo);
return;
}
this.foo = foo;
}
}
```
#### 扩展EntityGraph
由于Spring Data JPA仅支持注解的形式配置EntityGraph,不支持运行的时候动态指定抓取的字段,推荐使用 spring-data-jpa-entity-graph 来扩展EntityGraph功能。
1. 引入maven依赖
```xml
<dependency>
<groupId>com.cosium.spring.data</groupId>
<artifactId>spring-data-jpa-entity-graph</artifactId>
<version>2.0.6</version>
</dependency>
```
2. 配置JPA reposition
```java
@Configuration
@EnableJpaRepositories(basePackages = "com.aiyun.hepl.repository", repositoryFactoryBeanClass = EntityGraphJpaRepositoryFactoryBean.class)
@EnableTransactionManagement
public class DatabaseConfiguration {
}
```
3. 修改 reposition 的继承接口。
```java
// 原repository
public interface FooRepository extends JpaRepository<Foo, Long> {
}
// 新reposition
public interface FooRepository extends EntityGraphJpaRepository<Foo, Long> {
}
```
注: 除了 EntityGraphJpaRepository,框架还提供EntityGraphJpaSpecificationExecutor、EntityGraphQuerydslPredicateExecutor、EntityGraphCrudRepository、EntityGraphPagingAndSortingRepository、EntityGraphQueryByExampleExecutor几个接口,根据实际情况使用。
4. 调用
除了支持JPA自带的注解外,EntityGraphJpaRepository支持使用com.cosium.spring.data.jpa.entity.graph.domain.EntityGraph类作为入参以实现动态抓取的目的。
```java
Page<Bar> page = barRepository.findAll(pageable, EntityGraphUtils.fromAttributePaths("foo", "user"));
```
Clone repository
  • Home
  • 优化Spring Data JPA关联数据读取效率