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

迁移Hibernate 6后复用Collection类型ParameterExpression抛NoSuchElementException

Hibernate 6中CriteriaQuery复用Collection类型ParameterExpression触发NoSuchElementException的解决方案

迁移到Hibernate 6后,在CriteriaQuery里复用Collection类型的ParameterExpression时会抛出NoSuchElementException,旧版本Hibernate没有这个问题。推测是参数对应的Iterator被多次遍历导致。

示例代码:

public List<Long> example(Long userId) {
    CriteriaBuilder builder = entityManager.getCriteriaBuilder();
    CriteriaQuery<Long> query = builder.createQuery(Long.class);
    Root<User> user = query.from(User.class);
    query.select(user.get(User_.id));
    ParameterExpression<Collection<Long>> userIdParam = builder.parameter(generify(Collection.class));
    query.where(
            builder.or(
                    user.get(User_.id).in(userIdParam),
                    user.get(User_.id).in(userIdParam)
            )
    );
    query.distinct(true);
    TypedQuery<Long> typedQuery = entityManager.createQuery(query);
    typedQuery.setParameter(userIdParam, Collections.singletonList(userId));
    return typedQuery.getResultList();
}

不想为每个条件重复创建相同的参数表达式,求解决方案。

异常堆栈:

java.util.NoSuchElementException: null
    at java.util.Collections$1.next(Collections.java:4817) ~[?:?]
    at org.hibernate.query.sqm.internal.SqmUtil.createJdbcParameterBindings(SqmUtil.java:267) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.buildCacheableSqmInterpretation(ConcreteSqmSelectQueryPlan.java:358) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.withCacheableSqmInterpretation(ConcreteSqmSelectQueryPlan.java:268) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.performList(ConcreteSqmSelectQueryPlan.java:244) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.query.sqm.internal.QuerySqmImpl.doList(QuerySqmImpl.java:518) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.query.spi.AbstractSelectionQuery.list(AbstractSelectionQuery.java:367) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.query.Query.getResultList(Query.java:119) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at jdk.internal.reflect.GeneratedMethodAccessor137.invoke(Unknown Source) ~[?:?]
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
    at java.lang.reflect.Method.invoke(Method.java:568) ~[?:?]
    at org.springframework.orm.jpa.SharedEntityManagerCreator$DeferredQueryInvocationHandler.invoke(SharedEntityManagerCreator.java:405) ~[spring-orm-6.0.10.jar:6.0.10]
    at jdk.proxy3.$Proxy156.getResultList(Unknown Source) ~[?:?]
    at com.example.dao.UserDao.example(UserDao.java:265) ~[example-dao-3.22.1.jar:?]
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[?:?]
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
    at java.lang.reflect.Method.invoke(Method.java:568) ~[?:?]
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343) ~[spring-aop-6.0.10.jar:6.0.10]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196) ~[spring-aop-6.0.10.jar:6.0.10]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-6.0.10.jar:6.0.10]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:756) ~[spring-aop-6.0.10.jar:6.0.10]
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137) ~[spring-tx-6.0.10.jar:6.0.10]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.10.jar:6.0.10]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:756) ~[spring-aop-6.0.10.jar:6.0.10]
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) ~[spring-aop-6.0.10.jar:6.0.10]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.10.jar:6.0.10]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:756) ~[spring-aop-6.0.10.jar:6.0.10]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-6.0.10.jar:6.0.10]
    at com.example.dao.UserDao$$SpringCGLIB$$0.example(<generated>) ~[example-dao-3.22.1.jar:?]

解决方案

1. 使用参数名称代替ParameterExpression

直接用字符串作为参数名,多次引用同一个名称,Hibernate只会处理一次参数绑定,避免Iterator被重复遍历:

public List<Long> example(Long userId) {
    CriteriaBuilder builder = entityManager.getCriteriaBuilder();
    CriteriaQuery<Long> query = builder.createQuery(Long.class);
    Root<User> user = query.from(User.class);
    query.select(user.get(User_.id));
    String userIdParamName = "userIdList";
    query.where(
            builder.or(
                    user.get(User_.id).in(builder.parameter(Collection.class, userIdParamName)),
                    user.get(User_.id).in(builder.parameter(Collection.class, userIdParamName))
            )
    );
    query.distinct(true);
    TypedQuery<Long> typedQuery = entityManager.createQuery(query);
    typedQuery.setParameter(userIdParamName, Collections.singletonList(userId));
    return typedQuery.getResultList();
}

2. 提取重复Predicate复用

把重复的条件封装成一个Predicate,再在or逻辑里复用这个Predicate,这样ParameterExpression只会被解析一次:

public List<Long> example(Long userId) {
    CriteriaBuilder builder = entityManager.getCriteriaBuilder();
    CriteriaQuery<Long> query = builder.createQuery(Long.class);
    Root<User> user = query.from(User.class);
    query.select(user.get(User_.id));
    ParameterExpression<Collection<Long>> userIdParam = builder.parameter(generify(Collection.class));
    Predicate idInParam = user.get(User_.id).in(userIdParam);
    query.where(
            builder.or(idInParam, idInParam)
    );
    query.distinct(true);
    TypedQuery<Long> typedQuery = entityManager.createQuery(query);
    typedQuery.setParameter(userIdParam, Collections.singletonList(userId));
    return typedQuery.getResultList();
}

3. 升级Hibernate版本

这个问题大概率是Hibernate 6某个版本的bug,后续版本可能已经修复。可以尝试升级到最新的稳定版(比如6.3.x及以上),验证是否解决问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 19:00:53