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

Double Array Hibernate @JdbcTypeCode(SqlTypes.ARRAY) causes NullPointerException with 6.6.0.Final #43056

Closed
dhoffer opened this issue Sep 5, 2024 · 9 comments · Fixed by #44391
Assignees
Labels
area/hibernate-orm Hibernate ORM area/jdbc Issues related to the JDBC extensions kind/bug Something isn't working
Milestone

Comments

@dhoffer
Copy link

dhoffer commented Sep 5, 2024

Describe the bug

We had previously upgraded our application to Quarkus 3.12.3 and the following entity mapping appeared to work fine, it did not cause errors at boot time.

@JdbcTypeCode(SqlTypes.ARRAY)
    @Column(name = "annLims")
    @Size(max = 10)
    @JsonView(Views.Abridged.class)
    private Integer[][] annLims;

However today I updated to Quarkus 3.14.2 which uses Hibernate 6.6.0.Final and the above entity mapping causes this fatal error at boot time.

Exception in thread "main" java.lang.ExceptionInInitializerError
        at java.base/jdk.internal.misc.Unsafe.ensureClassInitialized0(Native Method)
        at java.base/jdk.internal.misc.Unsafe.ensureClassInitialized(Unsafe.java:1160)
        at java.base/jdk.internal.reflect.MethodHandleAccessorFactory.ensureClassInitialized(MethodHandleAccessorFactory.java:300)
        at java.base/jdk.internal.reflect.MethodHandleAccessorFactory.newConstructorAccessor(MethodHandleAccessorFactory.java:103)
        at java.base/jdk.internal.reflect.ReflectionFactory.newConstructorAccessor(ReflectionFactory.java:200)
        at java.base/java.lang.reflect.Constructor.acquireConstructorAccessor(Constructor.java:549)
        at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
        at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:70)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:44)
        at com.bs.sdl.Main.main(Main.java:45)
Caused by: java.lang.RuntimeException: Failed to start quarkus
        at io.quarkus.runner.ApplicationImpl.<clinit>(Unknown Source)
        ... 11 more
Caused by: java.lang.NullPointerException
        at java.base/java.util.concurrent.ConcurrentHashMap.putVal(ConcurrentHashMap.java:1011)
        at java.base/java.util.concurrent.ConcurrentHashMap.put(ConcurrentHashMap.java:1006)
        at org.hibernate.type.BasicTypeRegistry.resolve(BasicTypeRegistry.java:217)
        at org.hibernate.type.BasicTypeRegistry.resolve(BasicTypeRegistry.java:158)
        at org.hibernate.boot.model.process.internal.InferredBasicValueResolver.from(InferredBasicValueResolver.java:199)
        at org.hibernate.mapping.BasicValue.resolution(BasicValue.java:647)
        at org.hibernate.mapping.BasicValue.buildResolution(BasicValue.java:498)
        at org.hibernate.mapping.BasicValue.resolve(BasicValue.java:350)
        at org.hibernate.mapping.BasicValue.resolve(BasicValue.java:340)
        at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.lambda$processValueResolvers$6(InFlightMetadataCollectorImpl.java:1827)
        at java.base/java.util.ArrayList.removeIf(ArrayList.java:1765)
        at java.base/java.util.ArrayList.removeIf(ArrayList.java:1743)
        at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processValueResolvers(InFlightMetadataCollectorImpl.java:1826)
        at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1812)
        at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:334)
        at io.quarkus.hibernate.orm.runtime.boot.FastBootMetadataBuilder.build(FastBootMetadataBuilder.java:410)
        at io.quarkus.hibernate.orm.runtime.PersistenceUnitsHolder.createMetadata(PersistenceUnitsHolder.java:101)
        at io.quarkus.hibernate.orm.runtime.PersistenceUnitsHolder.constructMetadataAdvance(PersistenceUnitsHolder.java:73)
        at io.quarkus.hibernate.orm.runtime.PersistenceUnitsHolder.initializeJpa(PersistenceUnitsHolder.java:40)
        at io.quarkus.hibernate.orm.runtime.HibernateOrmRecorder$1.created(HibernateOrmRecorder.java:78)
        at io.quarkus.arc.runtime.ArcRecorder.initBeanContainer(ArcRecorder.java:80)
        at io.quarkus.deployment.steps.ArcProcessor$notifyBeanContainerListeners1304312071.deploy_0(Unknown Source)
        at io.quarkus.deployment.steps.ArcProcessor$notifyBeanContainerListeners1304312071.deploy(Unknown Source)
        ... 12 more

Is this a regression in Hibernate? Or perhaps is there a new SqlTypes enum for double arrays? I did not see one.

Expected behavior

Expected no errors at boot time.

Actual behavior

java.lang.NullPointerException at boot time, stack trace is above.

How to Reproduce?

No response

Output of uname -a or ver

No response

Output of java -version

java 21.0.4 2024-07-16 LTS

Quarkus version or git rev

3.14.2

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

Apache Maven 3.9.6 (bc0240f3c744dd6b6ec2920b3cd08dcc295161ae)

Additional information

No response

@dhoffer dhoffer added the kind/bug Something isn't working label Sep 5, 2024
@quarkus-bot quarkus-bot bot added area/hibernate-orm Hibernate ORM area/jdbc Issues related to the JDBC extensions labels Sep 5, 2024
Copy link

quarkus-bot bot commented Sep 5, 2024

/cc @barreiro (jdbc), @gsmet (hibernate-orm), @yrodiere (hibernate-orm,jdbc)

@dhoffer
Copy link
Author

dhoffer commented Sep 5, 2024

I have confirmed this is a regression in Quarkus 3.14.2.

Not only does Quarkus boot with 3.12.3 but the JAX-RS & JPA layer works perfect with the entity annotations above. Specifically @JdbcTypeCode(SqlTypes.ARRAY) worked perfectly with double integer array data.

Also our annLims column is typed as _int4.

@yrodiere
Copy link
Member

yrodiere commented Sep 6, 2024

Hey,

Thanks for reporting.

Is this a regression in Hibernate?

Seems likely. Either that or a this is just a usecase that was not intended to work and happened to work by chance: I had a quick look and could not find tests about persisting arrays of arrays in Hibernate ORM.

What's this @JsonViews though? I suppose the problem appears even without that?

In any case, can you please set up a reproducer based on this and submit a bug report on the Hibernate Jira, linking it here?

@dhoffer
Copy link
Author

dhoffer commented Sep 6, 2024

The @JSONVIEW is a Jackson thing that lets us customize what fields are returned to the user during the JAX-RS serialization process so you can ignore that, not relevant for this bug. The key is just to add a few columns to one of your existing Hibernate tests and just make sure that Hibernate can read/write the single & multi-dimensional arrays correctly. This worked perfectly in Hibernate 5.x and 6.x through Quarkus 3.12.3 (not sure which version broke it, but does not work in 3.14.2.

@JdbcTypeCode(SqlTypes.ARRAY)
@Column(name = "annLims")
@Size(max = 10)
private Integer[][] annLims;

(with Postgres DB column of _int4)

@JdbcTypeCode(SqlTypes.ARRAY)
@Column(name = "annText")
@Size(max = 256)
private String[] annText;

(with Postgres DB column of _varchar)

We use a lot of single arrays, String, Integer, Double, etc But for double arrays the above is the only one I found but should work with any data type.

@yrodiere yrodiere added the triage/needs-reproducer We are waiting for a reproducer. label Sep 6, 2024
@dhoffer
Copy link
Author

dhoffer commented Sep 6, 2024

Here is a reproducer, As soon as I added the double array field the test framework failed.

`@Entity
@table(name = "test", schema = "public")
public class Foo {
@id
@column(name = "id", nullable = false, length = 36)
private String id;

@Column(name = "bar")
private String bar;

@JdbcTypeCode(SqlTypes.ARRAY)
@Column(name = "bars")
private String[] bars;

@Column(name = "integer")
@JdbcTypeCode(SqlTypes.ARRAY)
private Integer[][] integers;

public String getId() {
    return id;
}

public void setId(String id) {
    this.id = id;
}

public String getBar() {
    return bar;
}

public void setBar(String bar) {
    this.bar = bar;
}

public String[] getBars() {
    return bars;
}

public void setBars(String[] bars) {
    this.bars = bars;
}

public Integer[][] getIntegers() {
    return integers;
}

public void setIntegers(Integer[][] integers) {
    this.integers = integers;
}

}`

JPAUnitTestCase test case

` @test
void hhh123Test() throws Exception {

    EntityManager entityManager = entityManagerFactory.createEntityManager();

    {
        String id = UUID.randomUUID().toString();
        {
            entityManager.getTransaction().begin();
            Foo foo = new Foo();
            foo.setId(id);
            foo.setBar("test");
            entityManager.persist(foo);
            entityManager.getTransaction().commit();
        }

        {
            entityManager.getTransaction().begin();
            Foo foo = entityManager.find(Foo.class, id);
            String actual = foo.getBar();
            Assert.assertEquals("test", actual);
            entityManager.getTransaction().commit();
        }
    }

    {
        String id = UUID.randomUUID().toString();
        String[] bars = {"1", "2", "3"};
        {
            entityManager.getTransaction().begin();
            Foo foo = new Foo();
            foo.setId(id);
            foo.setBars(bars);
            entityManager.persist(foo);
            entityManager.getTransaction().commit();
        }

        {
            entityManager.getTransaction().begin();
            Foo foo = entityManager.find(Foo.class, id);
            String[] actual = foo.getBars();
            Assert.assertArrayEquals(bars, actual);
            entityManager.getTransaction().commit();
        }
    }

    {
        String id = UUID.randomUUID().toString();
        Integer[][] expected = new Integer[2][2];
        {
            entityManager.getTransaction().begin();
            // Do stuff...
            Foo foo = new Foo();
            foo.setId(id);
            expected[0] = new Integer[2];
            expected[0][0] = 1;
            expected[0][1] = 2;
            expected[1] = new Integer[2];
            expected[1][0] = 3;
            expected[1][1] = 4;
            foo.setIntegers(expected);
            entityManager.persist(foo);
            entityManager.getTransaction().commit();
        }

        {
            entityManager.getTransaction().begin();
            Foo foo = entityManager.find(Foo.class, id);
            Integer[][] actual = foo.getIntegers();
            Assert.assertArrayEquals(expected[0], actual[0]);
            Assert.assertArrayEquals(expected[1], actual[1]);

            entityManager.getTransaction().commit();
        }

    }
    entityManager.close();
}`

@yrodiere
Copy link
Member

yrodiere commented Sep 6, 2024

Thank you. Reported upstream as https://hibernate.atlassian.net/browse/HHH-18582

@yrodiere yrodiere added triage/upstream and removed triage/needs-reproducer We are waiting for a reproducer. labels Sep 6, 2024
@yrodiere
Copy link
Member

@dhoffer Please head over to https://hibernate.atlassian.net/browse/HHH-18582 if you want to make your case, because mapping arrays of arrays is being described as a non-feature (not intended to work) and apparently can't be made to work on ORM 6.5 with your reproducer.

@yrodiere yrodiere added the triage/needs-feedback We are waiting for feedback. label Sep 10, 2024
@dhoffer
Copy link
Author

dhoffer commented Sep 10, 2024

@yrodiere Okay I went to the hibernate link and made my case. It's always worked pre hibernate 6.6.0 and we have massive production databases using it so its a blocker for us.

@yrodiere yrodiere removed the triage/needs-feedback We are waiting for feedback. label Sep 18, 2024
@yrodiere
Copy link
Member

Status:

  • Double array mapping has never been supported out-of-the-box according to Hibernate ORM maintainers, only through user types. How to reproduce that sort of user types in Hibernate ORM 6.6+ is being discussed in the comments of https://hibernate.atlassian.net/browse/HHH-18582.
  • https://hibernate.atlassian.net/browse/HHH-18582 will be fixed in ORM 6.6.2 / 7.0, but only by issuing a meaningful exception rather than an NPE. We can close the present issue upon upgrading to Hibernate ORM 6.6.2 / 7.0.

@yrodiere yrodiere self-assigned this Nov 18, 2024
@quarkus-bot quarkus-bot bot added this to the 3.18 - main milestone Nov 23, 2024
@gsmet gsmet modified the milestones: 3.18 - main, 3.17.1 Nov 27, 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 area/jdbc Issues related to the JDBC extensions kind/bug Something isn't working
Projects
None yet
3 participants