Hibernate JPA CriteriaQuery ParameterExpression<Collection> 使用多次 - NoSuchElementException

mas*_*ter 7 java hibernate jpa

迁移到 Hibernate 6 后,ParameterExpressionCollection类型一起使用会导致NoSuchElementException. 似乎Iterator每个参数都创建了一个,并且某些东西尝试多次遍历它。它可以在 Hibernate 的早期版本中运行。以一个非常虚拟的查询为例:

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();
}
Run Code Online (Sandbox Code Playgroud)

为每个条件创建 N 个相同的参数表达式有点麻烦。有什么建议吗?

堆栈跟踪:

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:?]
Run Code Online (Sandbox Code Playgroud)

Ser*_*nov 0

我认为这种行为是故意引入的,以显示查询中可能存在的逻辑错误。在您的演示示例中,您加入了两个相同的谓词,or这显然是误用的。

然后想象一下您收到作为方法参数的集合ids并在两个不同的谓词中使用它的情况。我同意在某些情况下这可能是有效的行为和程序员的意图,但在大多数情况下我认为这将是一个错误,并且NSEE至少会引起开发人员对查询的注意。