在一对多级联关系中使用JOIN FETCH获取嵌套关联数据
问题
我的应用使用Spring Data JPA,数据库为DB2,表结构属于遗留系统无法修改。现有如下实体类:Account与Job为一对多关系,Job与Task为一对多关系。
@Data @Entity @Table(name = "ACCOUNT") public class Account { @EmbeddedId private AccountId id; @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "account_type", referencedColumnName = "ACCOUNT_TYPE") @JoinColumn(name = "account_number", referencedColumnName = "ACCOUNT_NUMBER") @JoinColumn(name = "location_code", referencedColumnName = "LOCATION_CODE") private List<Job> jobs; // More fields... } @Data @Entity @Table(name = "ACCOUNT_JOB") public class Job { @EmbeddedId private JobId id; @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "job_number", referencedColumnName = "JOB_NUMBER") @JoinColumn(name = "part_number", referencedColumnName = "PART_NUMBER") @JoinColumn(name = "job_date", referencedColumnName = "JOB_DATE") @JoinColumn(name = "account_type", referencedColumnName = "ACCOUNT_TYPE") @JoinColumn(name = "account_number", referencedColumnName = "ACCOUNT_NUMBER") @JoinColumn(name = "location_code", referencedColumnName = "LOCATION_CODE") private List<Task> tasks; } @Data @Entity @Table(name = "JOB_TASK") public class Task { @EmbeddedId private TaskId id; @Column(name = "AP_PAY_CODE") private String taskName; @Column(name = "TASK_STATUS_DATE") private Date taskStatusDate; }
我希望获取Account及其所有关联的Job与Task数据。若将关联的FetchType设置为EAGER,可通过Repository的findById方法传入AccountId获取完整数据,但多数场景下我仅需获取Account,其余数据需按需懒加载。因此打算编写自定义查询,使用JOIN FETCH一次性获取完整数据,而非使用findById,但不知如何编写支持子层级的JOIN FETCH查询语句。当前查询模板如下:
@Query("SELECT account FROM Account account JOIN FETCH account.jobs jobs /*WHAT TO ADD HERE*/ where account.id = :accountId") Optional<Account> getAccountData(@Param("accountId") AccountId accountId);
解决方案
要一次性获取Account、关联的Job以及Job关联的Task,可在JPQL中嵌套使用JOIN FETCH直接关联子层级的tasks集合,修改后的查询语句如下:
@Query("SELECT account FROM Account account " + "JOIN FETCH account.jobs jobs " + "JOIN FETCH jobs.tasks " + "WHERE account.id = :accountId") Optional<Account> getAccountData(@Param("accountId") AccountId accountId);
关键说明
- 因为Job与Task是一对多关系,通过
JOIN FETCH jobs.tasks可直接从已关联的jobs中抓取对应的tasks集合,无需额外别名(加别名如JOIN FETCH jobs.tasks tasks也可,不影响结果)。 - 该写法会生成包含ACCOUNT、ACCOUNT_JOB、JOB_TASK三张表的连接查询,一次性加载所有层级数据,避免懒加载带来的N+1查询问题。
- 此标准JPQL写法完全兼容DB2数据库,无需特殊方言处理。
注意事项
- 确保实体类中
jobs和tasks的FetchType为默认的LAZY(Spring Data JPA中@OneToMany默认就是LAZY),这样在不调用该自定义查询时,只会加载Account本身,符合按需加载需求。 - 由于是一对多关联,查询结果可能出现重复的Account和Job实例,但Spring Data JPA会自动合并重复实例,最终返回的Account对象中,每个Job都会包含对应的tasks集合,不会有重复数据。
内容的提问来源于stack exchange,提问作者user2094311
相关产品推荐
相关产品推荐

