Nullpointer wenn using setFirstResult and setMaxResults in diffrent combinations

Description

When using EntityManager Query setFirstResult and setMaxResults and exuxute the same query again, but just with setMaxResults it results in a NullPointer.

The problem is in the caching of the parameters when a same query is executed. There is a parameter validation here that look wrong:JdbcOperationQuerySelect.isCompatible

Current:
return value != (int) jdbcParameterBinding.getBindValue();

But should be:
return value == (int) jdbcParameterBinding.getBindValue();

java.lang.NullPointerException: Cannot invoke "java.lang.Integer.intValue()" because the return value of "org.hibernate.query.spi.Limit.getFirstRow()" is null
at org.hibernate.sql.ast.spi.AbstractSqlAstTranslator$OffsetReceivingParameterBinder.bindParameterValue(AbstractSqlAstTranslator.java:4120) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
at org.hibernate.sql.results.jdbc.internal.DeferredResultSetAccess.bindParameters(DeferredResultSetAccess.java:199) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
at org.hibernate.sql.results.jdbc.internal.DeferredResultSetAccess.executeQuery(DeferredResultSetAccess.java:228) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
at org.hibernate.sql.results.jdbc.internal.DeferredResultSetAccess.getResultSet(DeferredResultSetAccess.java:163) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
at org.hibernate.sql.results.jdbc.internal.JdbcValuesResultSetImpl.advanceNext(JdbcValuesResultSetImpl.java:254) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
at org.hibernate.sql.results.jdbc.internal.JdbcValuesResultSetImpl.processNext(JdbcValuesResultSetImpl.java:134) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
at org.hibernate.sql.results.jdbc.internal.AbstractJdbcValues.next(AbstractJdbcValues.java:19) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
at org.hibernate.sql.results.internal.RowProcessingStateStandardImpl.next(RowProcessingStateStandardImpl.java:66) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
at org.hibernate.sql.results.spi.ListResultsConsumer.consume(ListResultsConsumer.java:198) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
at org.hibernate.sql.results.spi.ListResultsConsumer.consume(ListResultsConsumer.java:33) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.doExecuteQuery(JdbcSelectExecutorStandardImpl.java:361) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.executeQuery(JdbcSelectExecutorStandardImpl.java:168) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.list(JdbcSelectExecutorStandardImpl.java:93) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
at org.hibernate.sql.exec.spi.JdbcSelectExecutor.list(JdbcSelectExecutor.java:31) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.lambda$new$0(ConcreteSqmSelectQueryPlan.java:110) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.withCacheableSqmInterpretation(ConcreteSqmSelectQueryPlan.java:303) ~[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]

 

 

@Test public void testMaxAndLimit_HQL_BUG() { EntityManager entityManager = Services.getEntityManager(); TypedQuery<MitarbeiterEntity> query = entityManager.createQuery("select m from MitarbeiterEntity m", MitarbeiterEntity.class); query.setMaxResults(10); query.getResultList(); query = entityManager.createQuery("select m from MitarbeiterEntity m", MitarbeiterEntity.class); query.setMaxResults(10); query.setFirstResult(2); query.getResultList(); query = entityManager.createQuery("select m from MitarbeiterEntity m", MitarbeiterEntity.class); query.setMaxResults(10); query.getResultList(); }

Attachments

2
  • 10 Aug 2023, 08:48 AM
  • 10 Aug 2023, 08:48 AM

Activity

Show:

Jan Schatteman August 15, 2023 at 12:36 PM

It took some divine ( winking face ) intervention to be able to simulate the failure, but once that was possible the solution was easy. Btw your suspición was absolutely right, thanks for your assistance in this.

Jan Schatteman August 10, 2023 at 8:53 PM

I’m scratching my head on this one; I’m still unable to reproduce the issue. That said, I must admit that I don’t have access to a Cache DB, so i tried first with the SybaseASEDialect (which also uses the TLH), and also tried to ‘fake’ it by configuring the TopLimitHandler to the H2Dialect, and tried it in 6.2.5, but still to no avail. So either the TopLimitHandler is not the culprit in this story, or there is something else that I’m missing in the picture.

Also, during debugging I saw that it never goes into that if you mentioned, are you sure this is the snippet you wanted to refer to?

if ( localCopy.jdbcSelect.dependsOnParameterBindings() ) { jdbcParameterBindings = createJdbcParameterBindings( localCopy, executionContext ); }

Leopold Odenthal August 10, 2023 at 8:46 AM
Edited

This issue is also related to the dialect and LimitHandler being used.
We are using org.hibernate.community.dialect.CacheDialect that using a TopLimitHandler.

Depending on whether the code enters the if condition in org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan#withCacheableSqmInterpretation

else { // If the translation depends on parameter bindings or it isn't compatible with the current query options, // we have to rebuild the JdbcSelect, which is still better than having to translate from SQM to SQL AST again if ( localCopy.jdbcSelect.dependsOnParameterBindings() ) { jdbcParameterBindings = createJdbcParameterBindings( localCopy, executionContext ); }

I've added a test that reproduces the issue. With H2, for instance, it works since a different limit handler is used, and the code that triggers the error isn't executed.

Jan Schatteman August 1, 2023 at 6:13 PM

Adding to my first comment, I would advise you to try in ‘pure’ Hibernate, i.e. without the Spring layer in between.

Jan Schatteman July 28, 2023 at 6:49 PM

I have to say, I added your test (see the PR), but i couldn’t reproduce the issue in main, nor in 6.2.5, everything seems to work correctly (at least that NPE doesn’t happen). Is there perhaps something missing in the test above (e.g. in the way how the entitymanager is obtained, … ) ?

Fixed

Details

Assignee

Reporter

Components

Sprint

Fix versions

Affects versions

Priority

Created July 27, 2023 at 6:21 AM
Updated August 31, 2023 at 5:28 PM
Resolved August 15, 2023 at 12:36 PM

Flag notifications