Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NPE when fetching entity containing OneToOne property joined by multiple columns from the non-owning side #41700

Closed
DeKoxD opened this issue Jul 4, 2024 · 6 comments · Fixed by #41359
Labels
area/hibernate-orm Hibernate ORM kind/bug Something isn't working triage/upstream Used for issues which are caused by issues in upstream projects/dependency
Milestone

Comments

@DeKoxD
Copy link

DeKoxD commented Jul 4, 2024

Describe the bug

I started after I updated my project from Quarkus 3.8.5 to 3.12.0 (and .1), but this error also happens on 3.11.x.
Every entity with a property mapped by OneToOne using @JoinColumns started failing to load from the non-owning side (the mappedBy side).

Example Entity A:

@Entity
@Table(name = "EntityA", uniqueConstraints = @UniqueConstraint(columnNames = {"columnAEntityA", "columnBEntityA"}))
public class EntityA extends BaseEntity {
    @Column(name = "columnAEntityA")
    public Long columnAEntityA;
    @Column(name = "columnBEntityA")
    public Long columnBEntityA;

    @OneToOne(mappedBy = "entityA")
    public EntityB entityB;
}

Example Entity B:
Example:

@Entity
@Table(name = "EntityB", uniqueConstraints = @UniqueConstraint(columnNames = {"columnAEntityB", "columnBEntityB"}))
public class EntityB extends BaseEntity {
    @JoinColumn(name = "columnAEntityB")
    public Long columnAEntityB;
    @JoinColumn(name = "columnBEntityB")
    public Long columnBEntityB;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumns({
            @JoinColumn(name = "columnAEntityB", referencedColumnName = "columnAEntityA", insertable = false, updatable = false),
            @JoinColumn(name = "columnBEntityB", referencedColumnName = "columnBEntityA", insertable = false, updatable = false)
    })
    public EntityA entityA;
}

Expected behavior

No response

Actual behavior

I get the following error:

2024-07-04 17:35:53,254 WARNING [cor.exc.han.RuntimeExceptionHandler] (executor-thread-13) Runtime exception handled: java.lang.NullPointerException: Cannot invoke "Object.hashCode()" because "value" is null
	at org.hibernate.type.descriptor.java.AbstractClassJavaType.extractHashCode(AbstractClassJavaType.java:93)
	at org.hibernate.type.AbstractStandardBasicType.getHashCode(AbstractStandardBasicType.java:217)
	at org.hibernate.type.AbstractStandardBasicType.getHashCode(AbstractStandardBasicType.java:226)
	at org.hibernate.type.EntityType.getHashCode(EntityType.java:362)
	at org.hibernate.engine.spi.EntityUniqueKey.generateHashCode(EntityUniqueKey.java:67)
	at org.hibernate.engine.spi.EntityUniqueKey.<init>(EntityUniqueKey.java:48)
	at org.hibernate.sql.results.graph.entity.internal.EntitySelectFetchByUniqueKeyInitializer.initializeInstance(EntitySelectFetchByUniqueKeyInitializer.java:91)
	at org.hibernate.sql.results.internal.InitializersList.initializeInstance(InitializersList.java:73)
	at org.hibernate.sql.results.internal.StandardRowReader.coordinateInitializers(StandardRowReader.java:113)
	at org.hibernate.sql.results.internal.StandardRowReader.readRow(StandardRowReader.java:87)
	at org.hibernate.sql.results.spi.ListResultsConsumer.consume(ListResultsConsumer.java:205)
	at org.hibernate.sql.results.spi.ListResultsConsumer.consume(ListResultsConsumer.java:33)
	at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.doExecuteQuery(JdbcSelectExecutorStandardImpl.java:211)
	at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.executeQuery(JdbcSelectExecutorStandardImpl.java:83)
	at org.hibernate.sql.exec.spi.JdbcSelectExecutor.list(JdbcSelectExecutor.java:76)
	at org.hibernate.sql.exec.spi.JdbcSelectExecutor.list(JdbcSelectExecutor.java:65)
	at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.lambda$new$2(ConcreteSqmSelectQueryPlan.java:139)
	at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.withCacheableSqmInterpretation(ConcreteSqmSelectQueryPlan.java:382)
	at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.performList(ConcreteSqmSelectQueryPlan.java:302)
	at org.hibernate.query.sqm.internal.QuerySqmImpl.doList(QuerySqmImpl.java:526)
	at org.hibernate.query.spi.AbstractSelectionQuery.list(AbstractSelectionQuery.java:423)
	at org.hibernate.query.Query.getResultList(Query.java:120)
	at io.quarkus.hibernate.orm.panache.common.runtime.CommonPanacheQueryImpl.firstResult(CommonPanacheQueryImpl.java:316)
	at io.quarkus.hibernate.orm.panache.kotlin.runtime.PanacheQueryImpl.firstResult(PanacheQueryImpl.kt:123)
	...

How to Reproduce?

No response

Output of uname -a or ver

Linux pop-os 6.8.0-76060800daily20240311-generic #202403110203171019808822.04~1a3dbc7 SMP PREEMPT_DYNAMIC Mon M x86_64 x86_64 x86_64 GNU/Linux

Output of java -version

openjdk version "21.0.2" 2024-01-16 LTS OpenJDK Runtime Environment Temurin-21.0.2+13 (build 21.0.2+13-LTS) OpenJDK 64-Bit Server VM Temurin-21.0.2+13 (build 21.0.2+13-LTS, mixed mode, sharing)

Quarkus version or git rev

3.12.0

Build tool (ie. output of mvnw --version or gradlew --version)

Apache Maven 3.9.6 (bc0240f3c744dd6b6ec2920b3cd08dcc295161ae)

Additional information

As far as I dived in, I can see when it loads EntityA, it initializes its relations (EntitySelectFetchInitializer::initializeInstance). It tries to generate a key (EntityUniqueKey) for the inverse relation by passing a blank instance of the entity (EntityA<null>), where it tries to generate a hashCode (EntityUniqueKey::hashCode) from the null id.
Also, it now creates a EntitySelectFetchByUniqueKeyInitializer instead of a EntityDelayedFetchInitializer like before for entityA (see InitializersList).

If I have both @OneToOne with @JoinColumns on both sides (EntityA and EntityB), the error does not happen.

@DeKoxD DeKoxD added the kind/bug Something isn't working label Jul 4, 2024
Copy link

quarkus-bot bot commented Jul 4, 2024

/cc @geoand (kotlin)

@geoand geoand added area/hibernate-orm Hibernate ORM and removed area/kotlin labels Jul 4, 2024
@geoand
Copy link
Contributor

geoand commented Jul 4, 2024

cc @yrodiere

@yrodiere
Copy link
Member

yrodiere commented Jul 5, 2024

Hi,

Do you have any reason to believe this problem is caused by Quarkus itself?

If not, could you please create a reproducer based on https://github.com/hibernate/hibernate-test-case-templates/blob/main/orm/hibernate-orm-6/src/test/java/org/hibernate/bugs/QuarkusLikeORMUnitTestCase.java, and report this on the Hibernate JIRA (unless it's already fixed in Hibernate ORM 6.6.0.CR1)?

Thank you.

@yrodiere yrodiere added the triage/needs-feedback We are waiting for feedback. label Jul 5, 2024
@DeKoxD
Copy link
Author

DeKoxD commented Jul 5, 2024

I am not certain if it is or is not caused by Quarkus, so I generated two projects and added tests finding EntityA (non-owning) and EntityB (owning) separately:

  • hibernate-bug-test
    • Hibernate 6.5.2 only
    • reproducer test as you've said
    • results:
      • Find EntityA: Success
      • Find EntityB: Success
  • quarkus-bug-test
    • Quarkus 3.12.1 (using Hibernate 6.5.2)
    • similar reproducer, written in a more Quarkus-y way
    • results:
      • Find EntityA: Fail (same error as described before)
      • Find EntityB: Success

onetoone-bug-test.zip

Actually, I was going back testing some older Quarkus versions down to 3.7.4 and this error still happens, so I am not sure why I am only seeing it now. Anyway, it is breaking using Quarkus but it is not when using Hibernate only.

Thanks

@yrodiere
Copy link
Member

Thanks for the reproducers.

Anyway, it is breaking using Quarkus but it is not when using Hibernate only.

There's a difference between your Quarkus reproducer and the Hibernate-only reproducer: you're not using generated IDs on the Hibernate-only reproducer.

Adding @GeneratedValue to BaseEntity#id (and adjusting test setup/cleanup as well as ID expectations) leads to identical result the Hibernate-only reproducer.

So... this problem does indeed stem from a bug in Hibernate ORM.

@yrodiere yrodiere added triage/upstream Used for issues which are caused by issues in upstream projects/dependency and removed triage/needs-feedback We are waiting for feedback. labels Jul 15, 2024
@yrodiere
Copy link
Member

Reported upstream as https://hibernate.atlassian.net/browse/HHH-18390

@yrodiere yrodiere linked a pull request Aug 1, 2024 that will close this issue
@quarkus-bot quarkus-bot bot added this to the 3.14 - main milestone Aug 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/hibernate-orm Hibernate ORM kind/bug Something isn't working triage/upstream Used for issues which are caused by issues in upstream projects/dependency
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants