如何在HQL中正确编写非空多列属性校验查询?
解决HQL查询中的空值关联问题
问题描述
现有如下核心实体类结构:
class Payment implements Serializable { @Id private long id; @OneToOne(optional = true) private ProductPayee payeeId; @OneToOne(optional = true) private ProductAllocation allocationId; // payeeId非空时此值必为null // 其他字段... } class ProductPayee implements Serializable { @Id private long id; @OneToOne(optional = false) private ProductAllocation allocationId; // 其他字段... } class ProductAllocation implements Serializable { @Id private long id; @OneToOne(optional = true) private Product productId; // 此关联可能为空 // 其他字段... } // GroupLink、GroupStaff、ProductStaff实体类结构见原问题
需求是查询Payment表中payeeId或allocationId非空的数据:
- 当
payeeId非空时,校验其关联的ProductPayee属性 - 当
allocationId非空时,校验其关联的ProductAllocation属性
原HQL使用CASE表达式或COALESCE函数时,因关联链中存在空值(如ProductAllocation.productId为空)导致查询报错或丢失大量记录。
解决方案
核心思路是拆分条件分支,避免在单个表达式中访问可能为空的关联链,确保仅在关联存在时才访问其属性:
String hql = "FROM Payment WHERE (" + // 分支1:payeeId非空的情况 "payeeId IS NOT NULL AND (" + "(payeeId.allocationId.productId IS NOT NULL AND payeeId.allocationId.productId.id IN (" + "SELECT gl.productId.id FROM GroupLink gl " + "WHERE gl.groupId.id IN (" + "SELECT gs.groupId.id FROM GroupStaff gs WHERE gs.staffId.id = :staffId" + ")" + ")) OR " + "payeeId.allocationId.id IN (" + "SELECT ps.allocationId.id FROM ProductStaff ps " + "WHERE ps.staffId.id = :staffId AND ps.type = :resType" + ")" + ")) OR (" + // 分支2:allocationId非空的情况 "allocationId IS NOT NULL AND (" + "(allocationId.productId IS NOT NULL AND allocationId.productId.id IN (" + "SELECT gl.productId.id FROM GroupLink gl " + "WHERE gl.groupId.id IN (" + "SELECT gs.groupId.id FROM GroupStaff gs WHERE gs.staffId.id = :staffId" + ")" + ")) OR " + "allocationId.id IN (" + "SELECT ps.allocationId.id FROM ProductStaff ps " + "WHERE ps.staffId.id = :staffId AND ps.type = :resType" + ")" + ")) " + "ORDER BY date DESC"; Query query = session.createQuery(hql); query.setParameter("staffId", staffIdValue); query.setParameter("resType", resTypeValue);
关键优化点
- 分支隔离:将
payeeId非空和allocationId非空分为两个独立分支,符合业务中两者互斥的规则,避免交叉访问空关联。 - 空值前置判断:在访问
productId.id前,先判断productId IS NOT NULL,避免因关联为空导致的空指针异常。 - 子查询复用:两个分支复用相同的子查询逻辑,保证业务规则一致性。
补充说明
如果业务允许ProductAllocation.productId为空的记录被纳入查询(仅满足ProductStaff关联条件),可去掉对应的productId IS NOT NULL判断,仅保留productId.id IN (...)(此时空值会自动不满足IN条件,不会报错)。
内容的提问来源于stack exchange,提问作者KJEjava48
相关产品推荐
相关产品推荐

