You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Hibernate 5.5.x升级至6.1.7后鉴别器查询生成错误

Hibernate 5.5.x升级至6.1.7后鉴别器查询错误问题及解决方案

实体模型

@Entity
@Table(name = "PARTY")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "CLASS", discriminatorType = DiscriminatorType.STRING)
public class Party implements Serializable {

    @Id
    @GeneratedValue
    @Column(name = "PARTY_ID")
    private long partyID;

    @Column(name = "STATE")
    private String state;

    /*Lots of other columns NOT including CLASS though */
}

@Entity
@DiscriminatorValue("Organisation")
public class Organisation extends Party {
 
 @Column(name = "ORGANISATION_NAME", unique = true)
 private String name; 
}

@Entity
@DiscriminatorValue("Person")
public class Person extends Party {
  
  @OneToMany(cascade=CascadeType.ALL, mappedBy = "clientParty", fetch = FetchType.LAZY, orphanRemoval = true)
    @Fetch(value = FetchMode.SUBSELECT)
    private List<Relationship> relationships = new ArrayList<>();

}

@Entity
@Table(name = "RELATIONSHIP")
public class Relationship implements Serializable { 

    @Id
    @GeneratedValue
    @Column(name = "RELATIONSHIP_ID")
    private long relationshipId;

    @Column(name="CLIENT_PARTY_ID", insertable = false, updatable = false)
    private Long clientPartyId;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "CLIENT_PARTY_ID", nullable = false)
    private Person clientParty;

    @Column(name="SUPPLIER_PARTY_ID", insertable = false, updatable = false)
    private Long supplierPartyId;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "SUPPLIER_PARTY_ID", nullable = false)
    private Organisation supplierParty;
}

查询语句

用于获取状态为'active'或所属组织名称为'abc'的唯一用户的HQL:

SELECT DISTINCT u FROM Person u 
LEFT JOIN Relationship r ON u.partyID = r.clientPartyId 
LEFT JOIN Organisation o ON r.supplierPartyId = o.partyID 
WHERE lower(o.name) LIKE :orgName OR
lower(u.state) = :state

测试预期

预期返回3个用户:

  • 用户A - 状态'active',隶属于名称为'abc'的组织
  • 用户B - 状态'inactive',隶属于名称为'abc'的组织
  • 用户C - 状态'active',未隶属于任何组织

问题现象

Hibernate 5中该查询正常工作,但升级至6.1.7后,无关联关系的用户C被排除。

生成的SQL(Hibernate 6.1.7)

select distinct
m1_0.PARTY_ID from PARTY m1_0 
left join RELATIONSHIP r1_0 on m1_0.PARTY_ID=r1_0.CLIENT_PARTY_ID 
left join PARTY m2_0 on r1_0.SUPPLIER_PARTY_ID=m2_0.PARTY_ID 
where (lower(m2_0.ORGANISATION_NAME) like ? or lower(m1_0.STATE)=?) and
m1_0.CLASS='Person' and m2_0.CLASS='Organisation'

问题分析

Hibernate 6错误地将Organisation的鉴别器条件m2_0.CLASS='Organisation'放到了WHERE子句中,而非LEFT JOIN的ON关联条件里。这会导致LEFT JOIN退化为INNER JOIN的效果——没有关联组织的用户记录会因为m2_0.CLASS为NULL而被WHERE条件过滤掉。

解决方案

由于无法升级至6.2版本,可通过修改HQL手动将鉴别器条件移至LEFT JOIN的ON子句中,有两种写法:

写法1:使用HQL的type()函数(推荐)

利用HQL内置的type()函数指定实体类型,Hibernate会自动转换为对应的鉴别器条件:

SELECT DISTINCT u FROM Person u 
LEFT JOIN Relationship r ON u.partyID = r.clientPartyId 
LEFT JOIN Organisation o ON r.supplierPartyId = o.partyID AND type(o) = Organisation
WHERE (lower(o.name) LIKE :orgName OR lower(u.state) = :state)

写法2:直接指定鉴别器列

由于实体未映射CLASS字段,可直接在HQL中引用数据库列名:

SELECT DISTINCT u FROM Person u 
LEFT JOIN Relationship r ON u.partyID = r.clientPartyId 
LEFT JOIN Organisation o ON r.supplierPartyId = o.partyID AND o.CLASS = 'Organisation'
WHERE (lower(o.name) LIKE :orgName OR lower(u.state) = :state)

修改后的HQL生成的SQL会将鉴别器条件加入LEFT JOIN的ON子句,确保无关联关系的用户C不会被过滤,符合预期结果。

内容的提问来源于stack exchange,提问作者petula

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 09:53:20