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

QueryDSL关联无关联表左连接时添加关联表谓词触发空指针异常

问题:QueryDSL左连接查询添加UserRoles谓词后触发空指针异常

实体类定义

UsersStaging表实体

@Entity
@Table(name = "users_staging")
@Data
public class Usersstaging implements Serializable{
    private static final long serialVersionUID = 1L;
    
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "native")
    @GenericGenerator(name = "native", strategy = "native")
    @Column(name = "id", updatable = false, nullable = false)
    private Integer id;
    
    @Column(name = "employee_id", length = 10)
    private String employeeId;
    
    @Column(name="user_name")
    private String userName;
    
    @Column(name = "email")
    private String email;
}

UserRoles表实体

@Entity
@Table(name = "user_roles")
@Data
@TypeDefs({ @TypeDef(name = "json", typeClass = JsonType.class) })
public class UserRoles implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @Column(name = "employeeId", length = 10)
    private String employeeId;

    @Column(name = "username")
    private String username;

    @Type(type = "json")
    @Column(name = "roles", columnDefinition = "json")
    private List<String> roles;

}

问题场景

两张无关联的数据库表,使用QueryDSL基于username字段做左连接查询。仅添加UsersStaging表的谓词时查询正常,但加入UserRoles表的谓词(判断roles包含"SUPERADMIN")后触发空指针异常。

查询代码

JPAQuery<UsersStaging> query = new JPAQuery<>(entityManager);
QUsersStaging usersStagingPath = QUsersStaging.usersStaging;
QUserRoles userRolesPath = QUserRoles.userRoles;
BooleanExpression exp = usersStagingPath.legalFirstName
                .containsIgnoreCase(criteria.getValue());
BooleanExpression roleExp = userRolesPath.roles.any().eq("SUPERADMIN");
return query
        .select(Projections.constructor(UsersStagingDTO.class, usersStagingPath.employeeId,
                usersStagingPath.userName, usersStagingPath.email, usersStagingPath.isActive,
                usersStagingPath.legalFirstName, usersStagingPath.legalLastName, usersStagingPath.legalFullName,
                userRolesPath.roles))
        .from(usersStagingPath).leftJoin(userRolesPath).on(usersStagingPath.userName.eq(userRolesPath.username))
        .where(exp.and(roleExp)).limit(10).fetch();

异常信息

java.lang.NullPointerException: null
    at org.hibernate.hql.internal.ast.HqlSqlWalker.createFromJoinElement(HqlSqlWalker.java:432) ~[hibernate-core-5.6.10.Final.jar:5.6.10.Final]
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.joinElement(HqlSqlBaseWalker.java:4007) ~[hibernate-core-5.6.10.Final.jar:5.6.10.Final]
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.fromElement(HqlSqlBaseWalker.java:3793) ~[hibernate-core-5.6.10.Final.jar:5.6.10.Final]
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.fromElementList(HqlSqlBaseWalker.java:3671) ~[hibernate-core-5.6.10.Final.jar:5.6.10.Final]
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.fromClause(HqlSqlBaseWalker.java:746) ~[hibernate-core-5.6.10.Final.jar:5.6.10.Final]
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:602) ~[hibernate-core-5.6.10.Final.jar:5.6.10.Final]
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.collectionFunctionOrSubselect(HqlSqlBaseWalker.java:5020) ~[hibernate-core-5.6.10.Final.jar:5.6.10.Final]
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.comparisonExpr(HqlSqlBaseWalker.java:4713) ~[hibernate-core-5.6.10.Final.jar:5.6.10.Final]
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.logicalExpr(HqlSqlBaseWalker.java:2180) ~[hibernate-core-5.6.10.Final.jar:5.6.10.Final]
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.logicalExpr(HqlSqlBaseWalker.java:2108) ~[hibernate-core-5.6.10.Final.jar:5.6.10.Final]
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.whereClause(HqlSqlBaseWalker.java:841) ~[hibernate-core-5.6.10.Final.jar:5.6.10.Final]

解决方案

问题核心是左连接后直接在where子句过滤关联表数据,既破坏了左连接的特性,又因Hibernate处理JSON数组谓词时遇到null导致空指针。

方案1:将角色过滤条件移至左连接的on子句

保留左连接特性的同时过滤关联的UserRoles数据:

JPAQuery<UsersStaging> query = new JPAQuery<>(entityManager);
QUsersStaging usersStagingPath = QUsersStaging.usersStaging;
QUserRoles userRolesPath = QUserRoles.userRoles;
BooleanExpression exp = usersStagingPath.legalFirstName
                .containsIgnoreCase(criteria.getValue());
BooleanExpression roleExp = userRolesPath.roles.any().eq("SUPERADMIN");
return query
        .select(Projections.constructor(UsersStagingDTO.class, usersStagingPath.employeeId,
                usersStagingPath.userName, usersStagingPath.email, usersStagingPath.isActive,
                usersStagingPath.legalFirstName, usersStagingPath.legalLastName, usersStagingPath.legalFullName,
                userRolesPath.roles))
        .from(usersStagingPath)
        .leftJoin(userRolesPath)
            .on(usersStagingPath.userName.eq(userRolesPath.username).and(roleExp))
        .where(exp)
        .limit(10)
        .fetch();

方案2:给角色谓词添加非空判断

若必须在where子句过滤,需先判断关联表对象不为null:

BooleanExpression roleExp = userRolesPath.isNotNull().and(userRolesPath.roles.any().eq("SUPERADMIN"));

方案3:改用内连接(若需求允许)

如果只需要保留有对应SUPERADMIN角色的用户,直接用内连接更高效,无需额外判空:

.from(usersStagingPath)
.join(userRolesPath)
    .on(usersStagingPath.userName.eq(userRolesPath.username))
.where(exp.and(roleExp))

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 13:33:32