Hi, we have migrated our application to Hibernate 6 and we are in version 6.1.7.Final now. We have used the AliasToBeanResultTransformer in a couple of places to bind the resultset to a DTO object. We started to see some weird behavior in the transformation. First, we saw the resultant DTO object had only a few fields populated and most of the fields were left empty though the data was available in the database. It also happens only sporadically and also when there are multiple invocations of the same query in a short span of time. Secondly, we saw a few error messages that the cached aliases were different, but the compared array of aliases had the same values printed in the logs. The error was like below,
aliases are different from what is cached; aliases=[id, modificationTime, variable, instanceState, revision, value] cached=[id, modificationTime, variable, instanceState, revision, value]
java.lang.IllegalStateException: aliases are different from what is cached; aliases=[id, modificationTime, variable, instanceState, revision, value] cached=[id, modificationTime, variable, instanceState, revision, value]
at org.hibernate.transform.AliasToBeanResultTransformer.check(AliasToBeanResultTransformer.java:99) ~[hibernate-core-6.1.7.Final.jar!/:6.1.7.Final]
at org.hibernate.transform.AliasToBeanResultTransformer.transformTuple(AliasToBeanResultTransformer.java:59) ~[hibernate-core-6.1.7.Final.jar!/:6.1.7.Final]
at org.hibernate.sql.results.internal.RowTransformerTupleTransformerAdapter.transformRow(RowTransformerTupleTransformerAdapter.java:30) ~[hibernate-core-6.1.7.Final.jar!/:6.1.7.Final]
at org.hibernate.sql.results.internal.StandardRowReader.readRow(StandardRowReader.java:109) ~[hibernate-core-6.1.7.Final.jar!/:6.1.7.Final]
at org.hibernate.sql.results.spi.ListResultsConsumer.consume(ListResultsConsumer.java:198) ~[hibernate-core-6.1.7.Final.jar!/:6.1.7.Final]
at org.hibernate.sql.results.spi.ListResultsConsumer.consume(ListResultsConsumer.java:33) ~[hibernate-core-6.1.7.Final.jar!/:6.1.7.Final]
at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.doExecuteQuery(JdbcSelectExecutorStandardImpl.java:443) ~[hibernate-core-6.1.7.Final.jar!/:6.1.7.Final]
This was quite puzzling but when I debugged the AliasToBeanResultTransformer I was able to see the Setter[]
is maintained globally and populated only when the first row of the resultset is populated. The place where the setters are populated is not protected by a synchronous block. So when multiple threads access the transformTuple()
method for the first time the first thread populates the Setter[]
and then proceeds with setting the tuple values to the resultant object referring to the Setter[]
and in the meanwhile the second thread clears and repopulated the Setter[]
which caused some of the field of the resultant object not being populated by the first thread.
Below is how the AliasToBeanResultTransformer is used,
persistingControl.beginTx(tenant);
final Session session = m_persistingControl.getCurrentSession();
Query query = session.createQuery(QUERY_STRING);
query.setLockMode("e", LockMode.READ);
query.setFirstResult(OFFSET);
query.setMaxResults(SIZE);
query.setTupleTransformer(Transformers.aliasToBean(AInstanceDto.class));
List<T> output = query.list();
persistingControl.commit();
Is this already a known issue with Hibernate 6?
Because of the fact that a new instance of the AliasToBeanResultTransformer is always supplied to the query should it be really shared by multiple threads?
If there is a possibility that the transformer object can be accessed by multiple threads at a time shouldn’t it be either made stateless or the state is made thread-safe?