如何使用JPQL生成单条SQL一次性拉取所有关联实体(EclipseLink环境)
我正尝试使用JPQL生成单条SQL语句,一次性拉取所有通过外键关联的多个不同实体。
模型
模型结构如下:
@Entity @Table(name = "mainentity") @Data @IdClass(MainEntityPrimaryKey.class) public class MainEntity { @Id @Column(updatable = false) private String foo; @Id @Column(updatable = false) private String bar; @OneToMany(cascade = {CascadeType.ALL}, orphanRemoval = true) @JoinColumns({ @JoinColumn(name = "foo", referencedColumnName = "foo"), @JoinColumn(name = "bar", referencedColumnName = "bar") }) private List<SubEntity> subs; }
@Entity @Table(name = "subentity") @Data @IdClass(SubEntityPrimaryKey.class) public class SubEntity { @Id @Column(updatable = false) private String foo; @Id @Column(updatable = false) private String bar; @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "first_possibility_id", referencedColumnName = "first_possibility") private FirstPossibility firstPossibility; @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "second_possibility_id", referencedColumnName = "second_possibility") private SecondPossibility secondPossibility; }
@Entity @Table(name = "firstpossibility") @Data public class FirstPossibility { @Id @Column(name = "first_possibility_id", nullable = false, updatable = false) private String id; @Column private String something; }
@Entity @Table(name = "secondpossibility") @Data public class SecondPossibility { @Id @Column(name = "second_possibility_id", nullable = false, updatable = false) private String id; @Column private String stuff; }
单个SubEntity不会同时关联FirstPossibility和SecondPossibility。
仓库
Spring Data接口定义如下:
public interface MainEntityCRUDRepository extends CrudRepository<MainEntity, MainEntityPrimaryKey> { @Query("SELECT main FROM MainEntity main WHERE CONCAT(main.foo, '~', main.bar) IN :ids") List<MainEntity> getAllMainWithConcatenatedIds(@Param("ids") Collection<String> ids); }
当前状态
当前代码在开启spring.jpa.show-sql=true配置后,可以确认仅通过一次数据库调用就正确拉取了所有MainEntity,但并未拉取关联的SubEntity。SubEntity仅在后续代码访问时才会触发查询,同时还会拉取关联的First或Second实体,每个实体的查询都是独立的数据库调用,性能表现较差,是需要规避的问题。
重要说明:使用的JPA Provider为EclipseLink。
解决方案
直接在JPQL中使用LEFT JOIN FETCH声明需要同步抓取的关联实体即可,修改后的Repository查询代码如下:
public interface MainEntityCRUDRepository extends CrudRepository<MainEntity, MainEntityPrimaryKey> { @Query("SELECT main FROM MainEntity main " + "LEFT JOIN FETCH main.subs sub " + "LEFT JOIN FETCH sub.firstPossibility " + "LEFT JOIN FETCH sub.secondPossibility " + "WHERE CONCAT(main.foo, '~', main.bar) IN :ids") List<MainEntity> getAllMainWithConcatenatedIds(@Param("ids") Collection<String> ids); }
说明
JOIN FETCH会通知EclipseLink在查询主实体的同时,将指定关联实体的数据一并加载,所有数据通过单次SQL查询返回,不会触发后续懒加载的额外查询。- 使用
LEFT JOIN而非普通JOIN是为了避免关联字段为空时过滤掉上层实体,完全符合单个SubEntity仅关联一个可能性实体的业务场景。 - EclipseLink原生支持多关联JOIN FETCH,当前场景中仅存在一个集合属性
subs,剩余均为单值关联,不会出现重复数据或者抓取异常,可直接使用。
修改后重新开启SQL日志即可验证,生成的单条SQL会关联四张表做左连接,所有关联数据一次性返回,彻底解决N+1查询性能问题。
内容的提问来源于stack exchange,提问作者payne
相关产品推荐
相关产品推荐

