关联列不匹配时,如何避免Join Fetch后产生额外查询?
现有如下实体类,其中部分A实体的ref_b值在B表中没有匹配的行:
class A { @Id @Column(name = "id") Long id; @Column(name = "column_b") private String ref_b; @ManyToOne @JoinColumn(name = "column_b", referencedColumnName = "non_primary_column_in_b", insertable = false, updatable = false) private B b; } class B { @Id @Column(name = "id") Long id; @NaturalId @Column(name = "non_primary_column_in_b") String naturalId; }
调用以下仓库方法时,所有column_b与non_primary_column_in_b不匹配的A行都会触发额外查询:
@Query(""" from A a left join fetch a.b """) List<A> findAllA();
是否有办法避免这些额外查询?
我尝试过使用普通join替代join fetch,但必须保留那些在B表中无匹配的A行;也试过设置@ManyToOne的FetchType为EAGER和LAZY,均无效;给@ManyToOne添加@JsonIgnore后,Hibernate仍会生成额外查询。
这个问题的核心是Hibernate处理基于非主键字段的@ManyToOne关联时,对左连接未匹配到B的A实体,会尝试延迟加载关联对象,从而触发额外查询。以下是几种可行的解决方式:
1. 添加@NotFound(action = NotFoundAction.IGNORE)注解
在A类的b字段上添加该注解,明确告诉Hibernate:当找不到匹配的B实体时,直接将b设为null,无需触发额外查询:
@ManyToOne @JoinColumn(name = "column_b", referencedColumnName = "non_primary_column_in_b", insertable = false, updatable = false) @NotFound(action = NotFoundAction.IGNORE) private B b;
这个注解会让Hibernate在左连接查询后,直接将未匹配到的b属性置为null,从根源上避免后续的延迟加载查询。
2. 构造DTO返回结果
如果业务允许,可通过构造DTO(数据传输对象)的方式,只查询需要的字段,避免加载关联属性:
// 先定义ADto类,包含id、ref_b、b等所需字段 @Query(""" select new com.example.dto.ADto(a.id, a.ref_b, b) from A a left join a.b b """) List<ADto> findAllADto();
这种方式下,Hibernate会在单条SQL查询中获取所有需要的数据,不会触发额外查询。
3. 启用自然ID缓存(适合B数据稳定的场景)
由于B类的naturalId标注了@NaturalId,可以启用自然ID缓存,让Hibernate通过缓存判断是否存在匹配的B,避免无效的数据库查询:
首先配置B类的缓存:
@Entity @Cacheable @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_ONLY) class B { // 原有字段 @Id @Column(name = "id") Long id; @NaturalId @Column(name = "non_primary_column_in_b") String naturalId; }
然后在A类的关联上指定使用自然ID加载:
@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "column_b", referencedColumnName = "non_primary_column_in_b", insertable = false, updatable = false) @NaturalIdReference private B b;
该方案适合B表数据不常变更的场景,利用缓存减少无效查询。
内容的提问来源于stack exchange,提问作者snukluck

