如何使用Hibernate实现三张表的复杂关联并添加自定义JOIN条件
问题背景
现有三张核心业务表,核心字段如下:
- Format表
- id
- template_id
- parameterisation_id
- Param表
- name
- value
- parameterisation_id
- ParamDict表
- name
- template_id
需求为在Format和Param之间建立一对多关联,二者无直接关联字段,需要通过ParamDict中转关联,且关联时需额外满足Param.parameterisation_id = Format.parameterisation_id的校验条件。
现有实现缺陷
原有通过@JoinTable的关联配置如下:
@Entity @Table(schema = "123", name = "format") public class Format implements Serializable { @Id private Long id; private String template_id; private String parameterisation_id; @OneToMany(fetch = FetchType.EAGER) @JoinTable(name = "paramDict", schema = "123", joinColumns = {@JoinColumn(name = "template_id", referencedColumnName = "template_id",insertable = false,updatable = false)}, inverseJoinColumns = {@JoinColumn(name = "name", referencedColumnName = "name",insertable = false,updatable = false)}) private Set<Param> params = new HashSet<>(); }
该配置生成的SQL仅关联了name和template_id字段,没有加入parameterisation_id的校验逻辑:
Select * from Format f left join ParamDict pd on f.template_id = pd.template_id left join Param p on p.name = pd.name;
后续在Repository的@Query中增加where条件的方式虽然能生成逻辑正确的SQL,但Hibernate实体映射时会忽略额外的关联过滤条件,导致params集合中出现大量和当前Format不匹配的Param数据。
可行解决方案
方案1:关联过滤器实现(兼容所有Hibernate版本)
通过Hibernate自带的关联过滤注解,直接在关联定义处绑定额外校验逻辑,不需要修改查询语句:
- 首先在Format实体上定义过滤器,给params关联增加过滤条件:
import org.hibernate.annotations.Filter; import org.hibernate.annotations.FilterDef; import org.hibernate.annotations.ParamDef; @Entity @Table(schema = "123", name = "format") public class Format implements Serializable { @Id private Long id; private String template_id; private String parameterisation_id; @OneToMany(fetch = FetchType.EAGER) @JoinTable(name = "paramDict", schema = "123", joinColumns = {@JoinColumn(name = "template_id", referencedColumnName = "template_id",insertable = false,updatable = false)}, inverseJoinColumns = {@JoinColumn(name = "name", referencedColumnName = "name",insertable = false,updatable = false)}) // {alias}会被Hibernate自动替换为查询时Format表的别名,无需额外传参 @Filter(name = "paramParamIdMatch", condition = "parameterisation_id = {alias}.parameterisation_id") private Set<Param> params = new HashSet<>(); }
- 查询前开启过滤器即可:
public Set<Format> findByParameterisationIdIn(List<String> ids) { Session session = entityManager.unwrap(Session.class); session.enableFilter("paramParamIdMatch"); Set<Format> result = formatRepository.findBypParameterisationIdIn(ids); session.disableFilter("paramParamIdMatch"); return result; }
方案2:@JoinFormula实现(Hibernate 6+版本适用)
如果使用Hibernate 6及以上版本,可以直接在关联的join条件中添加自定义表达式,不需要额外配置过滤器:
@OneToMany(fetch = FetchType.EAGER) @JoinTable(name = "paramDict", schema = "123", joinColumns = {@JoinColumn(name = "template_id", referencedColumnName = "template_id",insertable = false,updatable = false)}, inverseJoinColumns = { @JoinColumn(name = "name", referencedColumnName = "name",insertable = false,updatable = false), // 直接把parameterisation_id匹配逻辑加入join条件 @JoinFormula(value = "parameterisation_id", referencedColumnName = "parameterisation_id") }) private Set<Param> params = new HashSet<>();
该方式会自动生成符合要求的关联SQL,映射时也会自动过滤不匹配的Param数据,不需要修改原有查询逻辑。
方案3:只读场景下@Formula实现
如果params集合仅用于查询、不需要新增/修改操作,可以直接通过子查询映射:
@Formula("(select p.* from param p join param_dict pd on p.name = pd.name where pd.template_id = template_id and p.parameterisation_id = parameterisation_id)") private Set<Param> params = new HashSet<>();
内容的提问来源于stack exchange,提问作者Илья Панкратов
相关产品推荐
相关产品推荐

