spring-projects / spring-projects/spring-data-commons

MappingException when persisting Entity that implements Collection [DATACMNS-1458]

Open
#1,889 0 comments 0 reactions 1 assignee View on GitHub

@odrotbohm is already working on this.

Since Dec 30, 2020.

in: mapping type: bug
Dominant language
Java
Stars
838
Forks
730
PR merge metrics
No merged PRs in 30d

Description

Michael A. Damone opened DATACMNS-1458 and commented

When trying to persist an Entity, using EntityManager.persist(Object), if that Entity also implements java.util.Collection then an exception is thrown:

org.springframework.data.mapping.MappingException: Couldn't find PersistentEntity for type java.lang.Object!
 at org.springframework.data.mapping.context.MappingContext.getRequiredPersistentEntity(MappingContext.java:119)
 at org.springframework.data.mapping.context.PersistentPropertyPathFactory.from(PersistentPropertyPathFactory.java:248)
 at org.springframework.data.mapping.context.PersistentPropertyPathFactory.from(PersistentPropertyPathFactory.java:176)
 at org.springframework.data.mapping.context.AbstractMappingContext.doFindPersistentPropertyPaths(AbstractMappingContext.java:322)
 at org.springframework.data.jpa.mapping.JpaMetamodelMappingContext.findPersistentPropertyPaths(JpaMetamodelMappingContext.java:100)
 at org.springframework.data.auditing.MappingAuditableBeanWrapperFactory$MappingAuditingMetadata.<init>(MappingAuditableBeanWrapperFactory.java:115)
 at org.springframework.data.auditing.MappingAuditableBeanWrapperFactory.lambda$null$0(MappingAuditableBeanWrapperFactory.java:83)
 at java.util.concurrent.ConcurrentMap.computeIfAbsent(ConcurrentMap.java:324)
 at org.springframework.data.auditing.MappingAuditableBeanWrapperFactory.lambda$null$1(MappingAuditableBeanWrapperFactory.java:82)
 at org.springframework.data.mapping.context.PersistentEntities.lambda$mapOnContext$4(PersistentEntities.java:115)
 at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
 at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175)
 at java.util.Spliterators$IteratorSpliterator.tryAdvance(Spliterators.java:1812)
 at java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:126)
 at java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:498)
 at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:485)
 at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
 at java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:152)
 at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
 at java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:464)
 at org.springframework.data.mapping.context.PersistentEntities.mapOnContext(PersistentEntities.java:116)
 at org.springframework.data.auditing.MappingAuditableBeanWrapperFactory.lambda$getBeanWrapperFor$3(MappingAuditableBeanWrapperFactory.java:80)
 at java.util.Optional.flatMap(Optional.java:241)
 at org.springframework.data.auditing.MappingAuditableBeanWrapperFactory.getBeanWrapperFor(MappingAuditableBeanWrapperFactory.java:74)
 at org.springframework.data.auditing.AuditingHandler.touch(AuditingHandler.java:161)
 at org.springframework.data.auditing.AuditingHandler.markCreated(AuditingHandler.java:131)
 at org.springframework.data.jpa.domain.support.AuditingEntityListener.touchForCreate(AuditingEntityListener.java:92)
 at java.lang.reflect.Method.invoke(Method.java:498)
 at org.hibernate.jpa.event.internal.ListenerCallback.performCallback(ListenerCallback.java:35)
 at org.hibernate.jpa.event.internal.CallbackRegistryImpl.callback(CallbackRegistryImpl.java:97)
 at org.hibernate.jpa.event.internal.CallbackRegistryImpl.preCreate(CallbackRegistryImpl.java:57)
 at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:116)
 at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:192)
 at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:135)
 at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:62)
 at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:800)
 at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:785)
 at java.lang.reflect.Method.invoke(Method.java:498)
 at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:308)

This used to work when using spring-boot 2.0.7.RELEASE, but I am trying to upgrade to spring-boot 2.1.1.RELEASE (which uses spring-data-jpa:2.1.3.RELEASE).

From what I can tell, there is a "new" PersistentPropertyPathFactory that now uses the "actualType" to determine what type the Entity is that needs to be persisted. However, the "actualType" for my Entity ends up just be java.lang.Object because it isCollectionLike (i.e. my type is a Collection so it satisfies the Collection.class.isAssignableFrom(rawType) check).

Here is a simplified example of the Entity/Collection class:

@Entity
public class ContainerCollection<C extends Container> extends Container implements Collection<C> {

    @JoinTable(
        name = "mod_container_collections",
        joinColumns = @JoinColumn(name = "container_collection_id"),
        inverseJoinColumns = @JoinColumn(name = "container_id"))
    protected Set<C> containers = new HashSet<>();

    @Override
    public int size() {
        return containers.size();
    }

    @Override
    public boolean isEmpty() {
        return containers.isEmpty();
    }

    // other Collection methods implemented to delegate to containers
    // with some relationship management in add(Object) and remove(Object)

}

And here is a really simple test that demonstrates the exception:

public class PersistenceTest {

    @PersistenceContext
    private EntityManager em;

    public void testPersist() {
        ContainerCollection<> collection = new ContainerCollection<>();
        em.persist(collection);  // this line throws MappingException
    }

}

Affects: 2.1.3 (Lovelace SR3)

Reference URL: https://stackoverflow.com/questions/53952858/how-do-i-persist-an-entity-which-implements-collection

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.