PropertyAccessException on owing side of bidirectional OneToOne

When migrating from Hibernate 6 to 7 we are in one case getting a org.hibernate.PropertyAccessException on the owing side of a OneToOne relation. The property in question is the optimistic locking version which is a primitive int int the model and NOT NULL on the database.

org.hibernate.PropertyAccessException: Null value was assigned to a property [class org.hibernate.bugs.Author.version] of primitive type: 'org.hibernate.bugs.Author.version' (setter)
at org.hibernate.property.access.spi.SetterFieldImpl.set(SetterFieldImpl.java:62)
at org.hibernate.persister.entity.AbstractEntityPersister.setPropertyValues(AbstractEntityPersister.java:4279)
at org.hibernate.persister.entity.EntityPersister.setValues(EntityPersister.java:1167)
at org.hibernate.sql.results.graph.entity.internal.EntityInitializerImpl.initializeEntityInstance(EntityInitializerImpl.java:1721)
at org.hibernate.sql.results.graph.entity.internal.EntityInitializerImpl.initializeInstance(EntityInitializerImpl.java:1607)
at org.hibernate.sql.results.graph.entity.internal.EntityInitializerImpl.initializeInstance(EntityInitializerImpl.java:88)
at org.hibernate.sql.results.internal.StandardRowReader.coordinateInitializers(StandardRowReader.java:239)
at org.hibernate.sql.results.internal.StandardRowReader.readRow(StandardRowReader.java:137)
at org.hibernate.sql.results.spi.ListResultsConsumer.readUnique(ListResultsConsumer.java:279)
at org.hibernate.sql.results.spi.ListResultsConsumer.readRows(ListResultsConsumer.java:227)
at org.hibernate.sql.results.spi.ListResultsConsumer.consume(ListResultsConsumer.java:164)
at org.hibernate.sql.results.spi.ListResultsConsumer.consume(ListResultsConsumer.java:31)
at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.doExecuteQuery(JdbcSelectExecutorStandardImpl.java:213)
at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.executeQuery(JdbcSelectExecutorStandardImpl.java:100)
at org.hibernate.sql.exec.spi.JdbcSelectExecutor.executeQuery(JdbcSelectExecutor.java:63)
at org.hibernate.sql.exec.spi.JdbcSelectExecutor.list(JdbcSelectExecutor.java:137)
at org.hibernate.sql.exec.spi.JdbcSelectExecutor.list(JdbcSelectExecutor.java:114)
at org.hibernate.sql.exec.spi.JdbcSelectExecutor.list(JdbcSelectExecutor.java:104)
at org.hibernate.loader.ast.internal.CollectionLoaderSingleKey.load(CollectionLoaderSingleKey.java:120)
at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:768)
at org.hibernate.event.internal.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:53)
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:138)
at org.hibernate.internal.SessionImpl.initializeCollection(SessionImpl.java:1750)
at org.hibernate.collection.spi.AbstractPersistentCollection.lambda$initialize$0(AbstractPersistentCollection.java:638)
at org.hibernate.collection.spi.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:284)
at org.hibernate.collection.spi.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:624)
at org.hibernate.collection.spi.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149)
at org.hibernate.collection.spi.PersistentBag.iterator(PersistentBag.java:419)

The only fix we have found so far is adding @Fetch(FetchMode.SELECT) to the optional side. We are struggling to create a reproducer even in our code base. So far it happens only in one system integration test. When we try to run the same code in a Java integration test then it works. We have been trying to debug the system integration test and what we can say is that ResultSet#wasNull() returns true for the version column, we were not yet able to identify what is returned for the primary key.

Here you can see a simplified version of our model. The tests pass. Yes, everything is EAGER.

Roughly out model is


Employer <- 1:N -> EmploymentLink 1:1 -> Person <-- 1:0..1 --> Author (PropertyAccessException version)

 

We are currently on ORM 7.4.1. We are not using bytecode enhancement.

Can you share the SQL queries that were generated and maybe also the values that were extracted by e.g. setting org.hibernate.orm.jdbc.bind=TRACE?

I can share the query of the reproducer, this works fine, the same way the query in the application works fine when run it alone.

    select
        e1_0.id,
        e1_0.version 
    from
        Employer e1_0 
    where
        e1_0.id=?
    select
        el1_0.employer_id,
        el1_0.id,
        el1_0.person_id,
        p1_0.id,
        a1_0.id, -- NULL
        a1_0.person_id, -- NULL
        a1_0.version, -- NULL
        p1_0.version,
        el1_0.version 
    from
        EmploymentLink el1_0 
    left join
        Person p1_0 
            on p1_0.id=el1_0.person_id 
    left join
        Author a1_0 
            on p1_0.id=a1_0.person_id 
    where
        el1_0.employer_id=?

That’s all pretty much as expected.

What we observe is that JdbcValuesResultSetImpl has the values in currentRowJdbcValues for Author (owning side of OneToOne) all set to null, including the primary key. However the EntityInitializerImpl is trying to initialize an entity for Author. This entity has the value of the primary key set from an EntityKey. We haven’t yet been able to find out where that EntityKey comes from. It is possible that somehow some earlier running query somehow makes the EntityKey available. That would explain why running the query itself works fine.

Currently it’s a bit difficult to enable query logging in our application, we’re working on this.