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关联数据读取效率

Last edited by 赵远 Aug 14, 2019
Page history

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

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

在使用Spring Data JPA经常会使用 ManyToOne、OneToMany、ManyToMany、OneToOne作为实体之间的关联,如果关联使用不当,很容易造成很严重的性能问题,下面会讲解不同场景下如何优化关联关系提升数据的读取效率。

OneToMany/ManyToMany 优化

OneToMany 默认会使用延迟加载策略加载 ToMany 数据,除非必须使用ToMany数据,否则必须使用延迟加载策略。

  1. 默认情况使用延迟加载
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;
}
  1. 如果必须在读取列表的时候加载ToMany数据,使用JPA的EntityGraph注解,禁止使用默认的抓取策略,会导致 1+N次查询问题,造成严重的性能损耗。
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,即不走代理,使用自定义的拦截器处理延迟加载。

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依赖
 <dependency>
     <groupId>com.cosium.spring.data</groupId>
     <artifactId>spring-data-jpa-entity-graph</artifactId>
     <version>2.0.6</version>
 </dependency>
  1. 配置JPA reposition
@Configuration
@EnableJpaRepositories(basePackages = "com.aiyun.hepl.repository", repositoryFactoryBeanClass = EntityGraphJpaRepositoryFactoryBean.class)
@EnableTransactionManagement
public class DatabaseConfiguration {
}
  1. 修改 reposition 的继承接口。
// 原repository
public interface FooRepository extends JpaRepository<Foo, Long> {

}

// 新reposition

public interface FooRepository extends EntityGraphJpaRepository<Foo, Long> {

}

注: 除了 EntityGraphJpaRepository,框架还提供EntityGraphJpaSpecificationExecutor、EntityGraphQuerydslPredicateExecutor、EntityGraphCrudRepository、EntityGraphPagingAndSortingRepository、EntityGraphQueryByExampleExecutor几个接口,根据实际情况使用。

  1. 调用

除了支持JPA自带的注解外,EntityGraphJpaRepository支持使用com.cosium.spring.data.jpa.entity.graph.domain.EntityGraph类作为入参以实现动态抓取的目的。

Page<Bar> page = barRepository.findAll(pageable, EntityGraphUtils.fromAttributePaths("foo", "user"));
Clone repository
  • Home
  • 优化Spring Data JPA关联数据读取效率