@Any and @ManyToAny Associations Fail in Spring Boot 3

I have been trying to setup a polymorphic relationship between some of the entities in a Spring Boot project for my employer. I recently updated the project to Spring Boot 3 so I could start using Hibernate 6’s improved custom collections. That’s working perfectly, but now I’m getting failures when trying to perist ANY polymorphic entities:

Cannot invoke "org.hibernate.metamodel.mapping.AttributeMapping.getAttributeMetadata()" because the return value of "org.hibernate.sql.results.graph.Fetchable.asAttributeMapping()" is null
java.lang.NullPointerException: Cannot invoke "org.hibernate.metamodel.mapping.AttributeMapping.getAttributeMetadata()" because the return value of "org.hibernate.sql.results.graph.Fetchable.asAttributeMapping()" is null
	at org.hibernate.loader.ast.internal.LoaderSelectBuilder.lambda$createFetchableBiConsumer$3(LoaderSelectBuilder.java:858)
	at org.hibernate.loader.ast.internal.LoaderSelectBuilder.visitFetches(LoaderSelectBuilder.java:764)
	at org.hibernate.loader.ast.internal.LoaderSqlAstCreationState.visitFetches(LoaderSqlAstCreationState.java:133)
	at org.hibernate.metamodel.mapping.internal.DiscriminatedAssociationMapping$AnyValuedResultGraphNode.afterInitialize(DiscriminatedAssociationMapping.java:382)
	at org.hibernate.metamodel.mapping.internal.DiscriminatedAssociationMapping$AnyValuedFetch.<init>(DiscriminatedAssociationMapping.java:501)
	at org.hibernate.metamodel.mapping.internal.DiscriminatedAssociationMapping.generateFetch(DiscriminatedAssociationMapping.java:339)
	at org.hibernate.metamodel.mapping.internal.DiscriminatedAssociationAttributeMapping.generateFetch(DiscriminatedAssociationAttributeMapping.java:138)
	at org.hibernate.sql.results.graph.FetchParent.generateFetchableFetch(FetchParent.java:108)
	at org.hibernate.loader.ast.internal.LoaderSelectBuilder.lambda$createFetchableBiConsumer$3(LoaderSelectBuilder.java:933)
	at org.hibernate.loader.ast.internal.LoaderSelectBuilder.visitFetches(LoaderSelectBuilder.java:764)
	at org.hibernate.loader.ast.internal.LoaderSqlAstCreationState.visitFetches(LoaderSqlAstCreationState.java:133)
	at org.hibernate.sql.results.graph.AbstractFetchParent.afterInitialize(AbstractFetchParent.java:32)
	at org.hibernate.sql.results.graph.entity.AbstractEntityResultGraphNode.afterInitialize(AbstractEntityResultGraphNode.java:65)
	at org.hibernate.persister.entity.AbstractEntityPersister.createDomainResult(AbstractEntityPersister.java:1204)
	at org.hibernate.loader.ast.internal.LoaderSelectBuilder.generateSelect(LoaderSelectBuilder.java:467)
	at org.hibernate.loader.ast.internal.LoaderSelectBuilder.createSelect(LoaderSelectBuilder.java:256)
	at org.hibernate.loader.ast.internal.SingleIdEntityLoaderStandardImpl.createLoadPlan(SingleIdEntityLoaderStandardImpl.java:180)
	at org.hibernate.loader.ast.internal.SingleIdEntityLoaderStandardImpl.resolveLoadPlan(SingleIdEntityLoaderStandardImpl.java:120)
	at org.hibernate.loader.ast.internal.SingleIdEntityLoaderStandardImpl.load(SingleIdEntityLoaderStandardImpl.java:66)
	at org.hibernate.persister.entity.AbstractEntityPersister.doLoad(AbstractEntityPersister.java:3381)
	at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3371)
	at org.hibernate.event.internal.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:602)
	at org.hibernate.event.internal.DefaultLoadEventListener.loadFromCacheOrDatasource(DefaultLoadEventListener.java:588)
	at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:557)
	at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:550)
	at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:202)
	at org.hibernate.event.internal.DefaultLoadEventListener.loadWithRegularProxy(DefaultLoadEventListener.java:282)
	at org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:237)
	at org.hibernate.event.internal.DefaultLoadEventListener.doOnLoad(DefaultLoadEventListener.java:106)
	at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:78)
	at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:138)
	at org.hibernate.internal.SessionImpl.fireLoadNoChecks(SessionImpl.java:1231)
	at org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1219)
	at org.hibernate.loader.internal.IdentifierLoadAccessImpl.doLoad(IdentifierLoadAccessImpl.java:194)
	at org.hibernate.loader.internal.IdentifierLoadAccessImpl.lambda$load$1(IdentifierLoadAccessImpl.java:160)
	at org.hibernate.loader.internal.IdentifierLoadAccessImpl.perform(IdentifierLoadAccessImpl.java:107)
	at org.hibernate.loader.internal.IdentifierLoadAccessImpl.load(IdentifierLoadAccessImpl.java:160)
	at org.hibernate.internal.SessionImpl.get(SessionImpl.java:1024)
	at org.hibernate.event.internal.DefaultMergeEventListener.lambda$entityIsDetached$0(DefaultMergeEventListener.java:321)
	at org.hibernate.engine.spi.LoadQueryInfluencers.fromInternalFetchProfile(LoadQueryInfluencers.java:79)
	at org.hibernate.event.internal.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:319)
	at org.hibernate.event.internal.DefaultMergeEventListener.merge(DefaultMergeEventListener.java:150)
	at org.hibernate.event.internal.DefaultMergeEventListener.doMerge(DefaultMergeEventListener.java:143)
	at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:127)
	at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:81)
	at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:127)
	at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:848)
	at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:834)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:311)
	at jdk.proxy3/jdk.proxy3.$Proxy103.merge(Unknown Source)
	at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:613)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.springframework.data.repository.core.support.RepositoryMethodInvoker$RepositoryFragmentMethodInvoker.lambda$new$0(RepositoryMethodInvoker.java:288)
	at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:136)
	at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:120)
	at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:516)
	at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:285)
	at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:628)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
	at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:168)
	at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:143)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
	at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:77)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
	at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:391)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
	at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
	at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:163)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:223)
	at jdk.proxy3/jdk.proxy3.$Proxy110.save(Unknown Source)
	at com.example.hibernatepolymorph.HibernatePolymorphApplicationTests.createPropertyHolder(HibernatePolymorphApplicationTests.java:55)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727)
...

To ensure it was not my code causing this problem, I created a blank Spring Boot 3.0.6 project, with Hibernate 6.1.7.Final, added the exact examples in the Hibernate documentation, and recreated the error on persiting either an @Any or @ManyToAny entity.

I then duplicated the same project, swapped it to Spring Boot 2.6.11, with Hibernate 5.6.15.Final, updated the example classes to fit the previous method for setting up polymorphic relationships, and persisting polymorphic entities worked immediately.

I forced the Spring Boot 3 project to use both Hibernate 6.0.2.Final and 6.2.2.Final, but they both gave me the same Fetchable.asAttributeMapping() is null problem. I also permuted the versions with Spring Boot 3.0.0, but now I get a different problem:

class org.hibernate.metamodel.mapping.internal.AnyDiscriminatorPart cannot be cast to class org.hibernate.metamodel.mapping.AttributeMapping (org.hibernate.metamodel.mapping.internal.AnyDiscriminatorPart and org.hibernate.metamodel.mapping.AttributeMapping are in unnamed module of loader 'app')
java.lang.ClassCastException: class org.hibernate.metamodel.mapping.internal.AnyDiscriminatorPart cannot be cast to class org.hibernate.metamodel.mapping.AttributeMapping (org.hibernate.metamodel.mapping.internal.AnyDiscriminatorPart and org.hibernate.metamodel.mapping.AttributeMapping are in unnamed module of loader 'app')

I can provide both of the sample projects, but basically, I can’t get an entity with an @Any or @ManyToAny to work with Hibernate 6 on Spring Boot 3. Can anyone tell me why?

A reproducer would be very helpful. Maybe you can attach it to "org.hibernate.sql.results.graph.Fetchable.asAttributeMapping()" is null since that sounds very similar to what you are reporting.

Yes, I saw that other post, however it doesn’t indicate for sure they are using Spring. I’ll reply to that one with a link to this comment.

I have created two repositories on GitHub containing the sample projects I mentioned above. One is using Spring Boot 2.7.11 (Hibernate 5.6.15.Final) while the other uses Spring Boot 3.0.6 (Hibernate 6.1.7.Final). They both contain the example classes from their respective Hibernate documentation for @Any and @ManyToAny associations and a single test class containing the tests from that documentation. Running a diff -r on them will show the only difference between the two repositories is the Spring Boot version and how the @Any relations are setup:

This is all you need to do to reproduce the failure using a Java 17 SDK:

git clone https://github.com/dirkniblick/polymorphOnSpringBoot2.git
cd polymorphOnSpringBoot2/
gradle test --info
cd ..
git clone https://github.com/dirkniblick/polymorphOnSpringBoot3.git
cd polymorphOnSpringBoot3/
gradle test --info

You will see the first one pass and the second one fail:

> Task :test FAILED

HibernatePolymorphApplicationTests > createPropertyRepository() FAILED
    java.lang.NullPointerException: Cannot invoke "org.hibernate.metamodel.mapping.AttributeMapping.getAttributeMetadataAccess()" because the return value of "org.hibernate.sql.results.graph.Fetchable.asAttributeMapping()" is null

We’d really need a reproducer that works without Spring, otherwise this could take a lot longer.

I went ahead and built a project to try and reproduce the error without Spring Boot. I could not:

The gradle test --info passed just fine. I pushed this over to the Spring Boot folks, but now I’m not sure how that other ticket is producing this error.

AFAICS from the stack trace, Spring uses EntityManager.merge() but your test uses persist. Try changing that on this line: polymorphWithoutSpringboot/HibernatePolymorphApplicationTests.java at develop · dirkniblick/polymorphWithoutSpringboot · GitHub

Here’s the save method executed by the saveAndFlush method on a Spring Repository:

	/*
	 * (non-Javadoc)
	 * @see org.springframework.data.repository.CrudRepository#save(java.lang.Object)
	 */
	@Transactional
	@Override
	public <S extends T> S save(S entity) {

		Assert.notNull(entity, "Entity must not be null.");

		if (entityInformation.isNew(entity)) {
			em.persist(entity);
			return entity;
		} else {
			return em.merge(entity);
		}
	}

Spring should only be using merge if the entity is not new. The entities saved here and here are brand new.

That said, I changed the test like so…

    @Test
    @Order(1)
    void createPropertyHolder() {
        ...
        PropertyHolder namePropertyHolder = new PropertyHolder();
        namePropertyHolder.setId(1L);
        save(namePropertyHolder);
        System.out.printf("Created: %s%n", namePropertyHolder);

        namePropertyHolder.setProperty(nameProperty);
        update(namePropertyHolder);
        System.out.printf("Updated: %s%n", namePropertyHolder);
        ...
    }

    @Test
    @Order(2)
    void createPropertyRepository() {
        ...
        PropertyRepository propertyRepository = new PropertyRepository();
        propertyRepository.setId(1L);

        save(propertyRepository);
        System.out.printf("Created: %s%n", propertyRepository);

        propertyRepository.getProperties().add(ageProperty);
        propertyRepository.getProperties().add(nameProperty);

        update(propertyRepository);
        System.out.printf("Updated: %s%n", propertyRepository);
        ...
    }

    void save(Object object) {
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();
        entityManager.persist(object);
        transaction.commit();
    }

    void update(Object object) {
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();
        entityManager.merge(object);
        transaction.commit();
    }

…and it still works. If it didn’t, that’d point the finger back at Hibernate. :wink:

Then I don’t know what is going on. At least the stack trace you posted shows that merge is called.

I’m trying to post a reply but they keep disappearing when I hit the Reply button…

Oh, yes, I’m sorry. That stacktrace was from my original application which was doing a merge. Here’s a stacktrace from Spring Boot 3 example project trying to save a new @Any entity:

Cannot invoke "org.hibernate.metamodel.mapping.AttributeMapping.getAttributeMetadataAccess()" because the return value of "org.hibernate.sql.results.graph.Fetchable.asAttributeMapping()" is null
java.lang.NullPointerException: Cannot invoke "org.hibernate.metamodel.mapping.AttributeMapping.getAttributeMetadataAccess()" because the return value of "org.hibernate.sql.results.graph.Fetchable.asAttributeMapping()" is null
	at org.hibernate.loader.ast.internal.LoaderSelectBuilder.lambda$createFetchableBiConsumer$6(LoaderSelectBuilder.java:786)
	at org.hibernate.loader.ast.internal.LoaderSelectBuilder.visitFetches(LoaderSelectBuilder.java:698)
	at org.hibernate.loader.ast.internal.LoaderSqlAstCreationState.visitFetches(LoaderSqlAstCreationState.java:118)
	at org.hibernate.metamodel.mapping.internal.DiscriminatedAssociationMapping$AnyValuedResultGraphNode.afterInitialize(DiscriminatedAssociationMapping.java:373)
	at org.hibernate.metamodel.mapping.internal.DiscriminatedAssociationMapping$AnyValuedFetch.<init>(DiscriminatedAssociationMapping.java:482)
	at org.hibernate.metamodel.mapping.internal.DiscriminatedAssociationMapping.generateFetch(DiscriminatedAssociationMapping.java:330)
	at org.hibernate.metamodel.mapping.internal.DiscriminatedAssociationAttributeMapping.generateFetch(DiscriminatedAssociationAttributeMapping.java:136)
	at org.hibernate.sql.results.graph.FetchParent.generateFetchableFetch(FetchParent.java:105)
	at org.hibernate.loader.ast.internal.LoaderSelectBuilder.lambda$createFetchableBiConsumer$6(LoaderSelectBuilder.java:860)
	at org.hibernate.loader.ast.internal.LoaderSelectBuilder.visitFetches(LoaderSelectBuilder.java:698)
	at org.hibernate.loader.ast.internal.LoaderSqlAstCreationState.visitFetches(LoaderSqlAstCreationState.java:118)
	at org.hibernate.sql.results.graph.AbstractFetchParent.afterInitialize(AbstractFetchParent.java:33)
	at org.hibernate.sql.results.graph.entity.AbstractEntityResultGraphNode.afterInitialize(AbstractEntityResultGraphNode.java:100)
	at org.hibernate.persister.entity.AbstractEntityPersister.createDomainResult(AbstractEntityPersister.java:1300)
	at org.hibernate.loader.ast.internal.LoaderSelectBuilder.generateSelect(LoaderSelectBuilder.java:450)
	at org.hibernate.loader.ast.internal.LoaderSelectBuilder.createSelect(LoaderSelectBuilder.java:177)
	at org.hibernate.loader.ast.internal.SingleIdEntityLoaderStandardImpl.createLoadPlan(SingleIdEntityLoaderStandardImpl.java:180)
	at org.hibernate.loader.ast.internal.SingleIdEntityLoaderStandardImpl.resolveLoadPlan(SingleIdEntityLoaderStandardImpl.java:120)
	at org.hibernate.loader.ast.internal.SingleIdEntityLoaderStandardImpl.load(SingleIdEntityLoaderStandardImpl.java:66)
	at org.hibernate.persister.entity.AbstractEntityPersister.doLoad(AbstractEntityPersister.java:4416)
	at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:4406)
	at org.hibernate.event.internal.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:590)
	at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:563)
	at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:221)
	at org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:358)
	at org.hibernate.event.internal.DefaultLoadEventListener.doOnLoad(DefaultLoadEventListener.java:110)
	at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:72)
	at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:118)
	at org.hibernate.internal.SessionImpl.fireLoadNoChecks(SessionImpl.java:1244)
	at org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1232)
	at org.hibernate.loader.access.IdentifierLoadAccessImpl.doLoad(IdentifierLoadAccessImpl.java:195)
	at org.hibernate.loader.access.IdentifierLoadAccessImpl.lambda$load$1(IdentifierLoadAccessImpl.java:161)
	at org.hibernate.loader.access.IdentifierLoadAccessImpl.perform(IdentifierLoadAccessImpl.java:108)
	at org.hibernate.loader.access.IdentifierLoadAccessImpl.load(IdentifierLoadAccessImpl.java:161)
	at org.hibernate.internal.SessionImpl.get(SessionImpl.java:1028)
	at org.hibernate.event.internal.DefaultMergeEventListener.lambda$entityIsDetached$0(DefaultMergeEventListener.java:344)
	at org.hibernate.engine.spi.LoadQueryInfluencers.fromInternalFetchProfile(LoadQueryInfluencers.java:79)
	at org.hibernate.event.internal.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:342)
	at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:178)
	at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:81)
	at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:107)
	at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:830)
	at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:816)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:311)
	at jdk.proxy3/jdk.proxy3.$Proxy104.merge(Unknown Source)
	at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:613)
	at org.springframework.data.jpa.repository.support.SimpleJpaRepository.saveAndFlush(SimpleJpaRepository.java:621)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
...

When saving a new @ManyToAny, the stacktrace is identical except for this:

...
	at org.hibernate.metamodel.mapping.internal.DiscriminatedAssociationMapping.generateFetch(DiscriminatedAssociationMapping.java:330)
	at org.hibernate.metamodel.mapping.internal.DiscriminatedCollectionPart.generateFetch(DiscriminatedCollectionPart.java:134)
	at org.hibernate.sql.results.graph.FetchParent.generateFetchableFetch(FetchParent.java:105)
	at org.hibernate.loader.ast.internal.LoaderSelectBuilder.lambda$createFetchableBiConsumer$6(LoaderSelectBuilder.java:860)
	at org.hibernate.loader.ast.internal.LoaderSelectBuilder.visitFetches(LoaderSelectBuilder.java:698)
	at org.hibernate.loader.ast.internal.LoaderSqlAstCreationState.visitFetches(LoaderSqlAstCreationState.java:118)
	at org.hibernate.sql.results.graph.collection.internal.EagerCollectionFetch.<init>(EagerCollectionFetch.java:107)
	at org.hibernate.metamodel.mapping.internal.PluralAttributeMappingImpl.generateFetch(PluralAttributeMappingImpl.java:395)
	at org.hibernate.sql.results.graph.FetchParent.generateFetchableFetch(FetchParent.java:105)
...

You are correct though. The SimpleJpaRepository.save() method is calling EntityManager.merge():

	at jdk.proxy3/jdk.proxy3.$Proxy104.merge(Unknown Source)
	at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:613)
	at org.springframework.data.jpa.repository.support.SimpleJpaRepository.saveAndFlush(SimpleJpaRepository.java:621)

That doesn’t seem right…

Try calling entityManager.clear() before save()/merge(). This might be the key to reproducing the problem.

As suggested, I added the clear() method to the save() method in the test:

    void save(Object object) {
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();
        entityManager.clear(); // Added
        entityManager.persist(object);
        transaction.commit();
    }

The first test works successfully, however the second test fails when trying to save the @ManyToAny entity. What’s interesting is the problem this time is new:

May 09, 2023 9:22:23 AM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
WARN: SQL Error: 23505, SQLState: 23505
May 09, 2023 9:22:23 AM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
ERROR: Unique index or primary key violation: "PRIMARY KEY ON PUBLIC.INTEGER_PROPERTY(ID) ( /* key:2 */ CAST(2 AS BIGINT), 'age', 23)"; SQL statement:
insert into integer_property ("name", "value", id) values (?, ?, ?) [23505-214]
May 09, 2023 9:22:23 AM org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl release
INFO: HHH000010: On release of batch it still contained JDBC statements

Error while committing the transaction
jakarta.persistence.RollbackException: Error while committing the transaction
	at app//org.hibernate.internal.ExceptionConverterImpl.convertCommitException(ExceptionConverterImpl.java:83)
	at app//org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:104)
	at app//com.example.hibernatepolymorph.HibernatePolymorphApplicationTests.save(HibernatePolymorphApplicationTests.java:119)
	at app//com.example.hibernatepolymorph.HibernatePolymorphApplicationTests.createPropertyRepository(HibernatePolymorphApplicationTests.java:99)
	at java.base@17.0.2/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base@17.0.2/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base@17.0.2/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base@17.0.2/java.lang.reflect.Method.invoke(Method.java:568)
	at app//org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727)
	at app//org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
	at app//org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
	at app//org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156)
	at app//org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:147)
	at app//org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:86)
	at app//org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103)
	at app//org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93)
	at app//org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
	at app//org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
	at app//org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
	at app//org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
	at app//org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92)
	at app//org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86)
	at app//org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:217)
	at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at app//org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:213)
	at app//org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:138)
	at app//org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68)
	at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
	at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at app//org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.base@17.0.2/java.util.ArrayList.forEach(ArrayList.java:1511)
	at app//org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at app//org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.base@17.0.2/java.util.ArrayList.forEach(ArrayList.java:1511)
	at app//org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at app//org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at app//org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
	at app//org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at app//org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
	at app//org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
	at app//org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
	at app//org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
	at app//org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
	at app//org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
	at app//org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
	at app//org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
	at app//org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:110)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:90)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:85)
	at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:62)
	at java.base@17.0.2/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base@17.0.2/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base@17.0.2/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base@17.0.2/java.lang.reflect.Method.invoke(Method.java:568)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
	at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
	at jdk.proxy1/jdk.proxy1.$Proxy2.stop(Unknown Source)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:193)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
	at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:113)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:65)
	at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
	at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
Caused by: jakarta.persistence.PersistenceException: Converting `org.hibernate.exception.ConstraintViolationException` to JPA `PersistenceException` : could not execute statement
	at app//org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:165)
	at app//org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:175)
	at app//org.hibernate.internal.ExceptionConverterImpl.convertCommitException(ExceptionConverterImpl.java:67)
	... 87 more
Caused by: org.hibernate.exception.ConstraintViolationException: could not execute statement
	at app//org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:60)
	at app//org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:56)
	at app//org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:109)
	at app//org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:95)
	at app//org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:200)
	at app//org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:39)
	at app//org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3438)
	at app//org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:4073)
	at app//org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:103)
	at app//org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:612)
	at app//org.hibernate.engine.spi.ActionQueue.lambda$executeActions$1(ActionQueue.java:483)
	at java.base@17.0.2/java.util.LinkedHashMap.forEach(LinkedHashMap.java:721)
	at app//org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:480)
	at app//org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:329)
	at app//org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39)
	at app//org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:107)
	at app//org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1425)
	at app//org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:477)
	at app//org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:2234)
	at app//org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:1930)
	at app//org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:439)
	at app//org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:183)
	at app//org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:281)
	at app//org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:101)
	... 86 more
Caused by: org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: Unique index or primary key violation: "PRIMARY KEY ON PUBLIC.INTEGER_PROPERTY(ID) ( /* key:2 */ CAST(2 AS BIGINT), 'age', 23)"; SQL statement:
insert into integer_property ("name", "value", id) values (?, ?, ?) [23505-214]
	at org.h2.message.DbException.getJdbcSQLException(DbException.java:508)
	at org.h2.message.DbException.getJdbcSQLException(DbException.java:477)
	at org.h2.message.DbException.get(DbException.java:223)
	at org.h2.message.DbException.get(DbException.java:199)
	at org.h2.mvstore.db.MVPrimaryIndex.add(MVPrimaryIndex.java:120)
	at org.h2.mvstore.db.MVTable.addRow(MVTable.java:519)
	at org.h2.command.dml.Insert.insertRows(Insert.java:174)
	at org.h2.command.dml.Insert.update(Insert.java:135)
	at org.h2.command.dml.DataChangeStatement.update(DataChangeStatement.java:74)
	at org.h2.command.CommandContainer.update(CommandContainer.java:169)
	at org.h2.command.Command.executeUpdate(Command.java:252)
	at org.h2.server.TcpServerThread.process(TcpServerThread.java:408)
	at org.h2.server.TcpServerThread.run(TcpServerThread.java:191)
	at java.base/java.lang.Thread.run(Thread.java:833)

	at app//org.h2.message.DbException.getJdbcSQLException(DbException.java:508)
	at app//org.h2.engine.SessionRemote.readException(SessionRemote.java:637)
	at app//org.h2.engine.SessionRemote.done(SessionRemote.java:606)
	at app//org.h2.command.CommandRemote.executeUpdate(CommandRemote.java:227)
	at app//org.h2.jdbc.JdbcPreparedStatement.executeUpdateInternal(JdbcPreparedStatement.java:209)
	at app//org.h2.jdbc.JdbcPreparedStatement.executeUpdate(JdbcPreparedStatement.java:169)
	at app//org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:197)
	... 105 more

May 09, 2023 9:22:23 AM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl$PoolState stop
INFO: HHH10001008: Cleaning up connection pool [jdbc:h2:tcp://localhost/~/test]

My mistake. I added the clear() method to the original version which wasn’t using merge(). I ran that same code with Hibernate 5.6.15Final and it provided the same exception.

However, I have updated the polymorphWithoutSpringboot repository to include the merge() method I posted on May 3rd with the clear() beforehand. Both tests give the same NullPointerException I originally posted:

  • gradle test --info --tests “HibernatePolymorphApplicationTests.createPropertyHolder”
  • gradle test --info --tests “HibernatePolymorphApplicationTests.createPropertyRepository”

I duplicated the entire project, but with Hibernate 5.6.15Final and the only other difference being how the @Any and @ManyToAny are implemented and the old Hibernate code works perfectly fine. Now, that makes me believe these exceptions in my Spring Boot 3 project are coming from Hibernate, not Spring.

Here’s the stacktrace:

HibernatePolymorphApplicationTests > createPropertyHolder() FAILED
    java.lang.NullPointerException: Cannot invoke "org.hibernate.metamodel.mapping.AttributeMapping.getAttributeMetadataAccess()" because the return value of "org.hibernate.sql.results.graph.Fetchable.asAttributeMapping()" is null
        at org.hibernate.loader.ast.internal.LoaderSelectBuilder.lambda$createFetchableBiConsumer$6(LoaderSelectBuilder.java:786)
        at org.hibernate.loader.ast.internal.LoaderSelectBuilder.visitFetches(LoaderSelectBuilder.java:698)
        at org.hibernate.loader.ast.internal.LoaderSqlAstCreationState.visitFetches(LoaderSqlAstCreationState.java:118)
        at org.hibernate.metamodel.mapping.internal.DiscriminatedAssociationMapping$AnyValuedResultGraphNode.afterInitialize(DiscriminatedAssociationMapping.java:373)
        at org.hibernate.metamodel.mapping.internal.DiscriminatedAssociationMapping$AnyValuedFetch.<init>(DiscriminatedAssociationMapping.java:482)
        at org.hibernate.metamodel.mapping.internal.DiscriminatedAssociationMapping.generateFetch(DiscriminatedAssociationMapping.java:330)
        at org.hibernate.metamodel.mapping.internal.DiscriminatedAssociationAttributeMapping.generateFetch(DiscriminatedAssociationAttributeMapping.java:136)
        at org.hibernate.sql.results.graph.FetchParent.generateFetchableFetch(FetchParent.java:105)
        at org.hibernate.loader.ast.internal.LoaderSelectBuilder.lambda$createFetchableBiConsumer$6(LoaderSelectBuilder.java:860)
        at org.hibernate.loader.ast.internal.LoaderSelectBuilder.visitFetches(LoaderSelectBuilder.java:698)
        at org.hibernate.loader.ast.internal.LoaderSqlAstCreationState.visitFetches(LoaderSqlAstCreationState.java:118)
        at org.hibernate.sql.results.graph.AbstractFetchParent.afterInitialize(AbstractFetchParent.java:33)
        at org.hibernate.sql.results.graph.entity.AbstractEntityResultGraphNode.afterInitialize(AbstractEntityResultGraphNode.java:100)
        at org.hibernate.persister.entity.AbstractEntityPersister.createDomainResult(AbstractEntityPersister.java:1300)
        at org.hibernate.loader.ast.internal.LoaderSelectBuilder.generateSelect(LoaderSelectBuilder.java:450)
        at org.hibernate.loader.ast.internal.LoaderSelectBuilder.createSelect(LoaderSelectBuilder.java:177)
        at org.hibernate.loader.ast.internal.SingleIdEntityLoaderStandardImpl.createLoadPlan(SingleIdEntityLoaderStandardImpl.java:180)
        at org.hibernate.loader.ast.internal.SingleIdEntityLoaderStandardImpl.resolveLoadPlan(SingleIdEntityLoaderStandardImpl.java:120)
        at org.hibernate.loader.ast.internal.SingleIdEntityLoaderStandardImpl.load(SingleIdEntityLoaderStandardImpl.java:66)
        at org.hibernate.persister.entity.AbstractEntityPersister.doLoad(AbstractEntityPersister.java:4416)
        at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:4406)
        at org.hibernate.event.internal.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:590)
        at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:563)
        at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:221)
        at org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:358)
        at org.hibernate.event.internal.DefaultLoadEventListener.doOnLoad(DefaultLoadEventListener.java:110)
        at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:72)
        at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:118)
        at org.hibernate.internal.SessionImpl.fireLoadNoChecks(SessionImpl.java:1244)
        at org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1232)
        at org.hibernate.loader.access.IdentifierLoadAccessImpl.doLoad(IdentifierLoadAccessImpl.java:195)
        at org.hibernate.loader.access.IdentifierLoadAccessImpl.lambda$load$1(IdentifierLoadAccessImpl.java:161)
        at org.hibernate.loader.access.IdentifierLoadAccessImpl.perform(IdentifierLoadAccessImpl.java:108)
        at org.hibernate.loader.access.IdentifierLoadAccessImpl.load(IdentifierLoadAccessImpl.java:161)
        at org.hibernate.internal.SessionImpl.get(SessionImpl.java:1028)
        at org.hibernate.event.internal.DefaultMergeEventListener.lambda$entityIsDetached$0(DefaultMergeEventListener.java:344)
        at org.hibernate.engine.spi.LoadQueryInfluencers.fromInternalFetchProfile(LoadQueryInfluencers.java:79)
        at org.hibernate.event.internal.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:342)
        at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:178)
        at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:81)
        at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:107)
        at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:830)
        at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:816)
        at com.example.hibernatepolymorph.HibernatePolymorphApplicationTests.update(HibernatePolymorphApplicationTests.java:130)
        at com.example.hibernatepolymorph.HibernatePolymorphApplicationTests.createPropertyHolder(HibernatePolymorphApplicationTests.java:64)

Alright, so you found two bugs. Please attach the non-Spring test case that reproduces the NPE on merge to [HHH-16532] - Hibernate JIRA.

If you think the FK constraint violation problem that you had before is a bug, please create a new JIRA for that and attach a test case for that.

I’m not sure how to go about attaching anything to that ticket. I am trying to sign into your Jira instance, but I never receive any of the verify emails it wants me to.

I added a comment to the JIRA issue linking to your repository, so this is fine. Can you please provide a reproducer for the constraint violation exception and then I’ll create the issue for you.

For anyone who finds this page, this was fixed it in Hibernate 6.2.4.Final.

I updated my test case with…

implementation 'org.hibernate.orm:hibernate-core:6.2.4.Final'

…and it works. I also updated it to use Spring Boot 3.1.1, which uses Hibernate 6.2.5.Final, and it works too. @beikov, thanks for your help troubleshooting this.