EclipseLink @MappedSuperclass and generics
Asked Answered
A

3

7

I have a few domain model classes in my web app that have a hierarchical relationship to themselves. An example of one is the hierarchical category structure used to classify users postings.

There is some logic relating to the hierarchical nature of these classes that is common. So I tried to move the logic into a generic @MappedSuperclass annotated superclass.

Something like :

@MappedSuperclass
public abstract class HierarchicalBaseEntity<N extends HierarchicalBaseEntity<N>>
        extends BaseEntity {

    @ManyToOne(optional=true)
    @JoinColumn(name="parent")
    private N parent;

    private int depth;

    public N getParent() { ...
    public void setParent(N newParent) { ...

    public boolean isRoot() { ...
    public int getDepth() { ...

    public boolean isDescendantOf(N ancestor) { ...
    public static <N extends HierarchicalBaseEntity<N>> N getCommonAncestor(N a, N b) { ...
    public static <N extends HierarchicalBaseEntity<N>> Collection<N> reduceToCommonAncestors(Collection<N> entities) { ...
}

The subclasses then extend HierarchicalBaseEntity giving themselves as the generic type N:

@Entity
public class CategoryBean extends HierarchicalBaseEntity<CategoryBean> {

In Java this all works out quite cleanly. But unfortunately EclipseLink doesn't seems to like the generic 'parent' field:

private N parent;

It gives the following exception:

Caused by: Exception [EclipseLink-7250] (Eclipse Persistence Services - 2.1.0.v20100614-r7608): org.eclipse.persistence.exceptions.ValidationException
Exception Description: [class net.timp.yaase.core.model.HierarchicalBaseEntity] uses a non-entity [class java.lang.String] as target entity in the relationship attribute [field parent].
at org.eclipse.persistence.exceptions.ValidationException.nonEntityTargetInRelationship(ValidationException.java:1341)
at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.RelationshipAccessor.getReferenceDescriptor(RelationshipAccessor.java:416)
at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.ObjectAccessor.processOneToOneForeignKeyRelationship(ObjectAccessor.java:609)
at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.ObjectAccessor.processOwningMappingKeys(ObjectAccessor.java:678)
at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.ManyToOneAccessor.process(ManyToOneAccessor.java:107)

Why is it complain about a non-entity String?

As a test I've tried removing the generics and just having the parent field defined as:

private HierarchicalBaseEntity parent;

Without generics, EclipseLink gave this exception:

Caused by: Exception [EclipseLink-7250] (Eclipse Persistence Services - 2.1.0.v20100614-r7608): org.eclipse.persistence.exceptions.ValidationException
Exception Description: [class net.timp.yaase.core.model.OnymBean] uses a non-entity [class net.timp.yaase.core.model.HierarchicalBaseEntity] as target entity in the relationship attribute [field parent].
at org.eclipse.persistence.exceptions.ValidationException.nonEntityTargetInRelationship(ValidationException.java:1341)
at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.RelationshipAccessor.getReferenceDescriptor(RelationshipAccessor.java:416)
at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.ObjectAccessor.processOneToOneForeignKeyRelationship(ObjectAccessor.java:609)
at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.ObjectAccessor.processOwningMappingKeys(ObjectAccessor.java:678)
at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.ManyToOneAccessor.process(ManyToOneAccessor.java:107)

True HierarchicalBaseEntity its not an Entity in either case, is a @MappedSuperclass.. but is there a way to do this with generics or otherwise? It seems you can't have a field in your @MappedSuperclass that references one of it's subclass.

Acosta answered 8/8, 2010 at 19:31 Comment(1)
This looks somehow similar to forums.sun.com/thread.jspa?threadID=5268944 (but the related issue is fixed). Could you try with another provider?Cyder
C
5

The issue is that when using Generics as field types for relationships EclipseLink can not know what the target type is until runtime when the actual instance is inspected. So the mapping would have to be dynamically created at runtime and this is not supported.

You could continue to use the Generic SuperClass but this would require moving the field to the Entities where they would have types defined then have abstract internal getters/setters for those fields that return Object that the generic methods would call casting to the generic type. Convoluted but it would allow for the Generic MappedSuperclass.

Charcuterie answered 9/8, 2010 at 13:14 Comment(4)
Hi Gordon, Thanks for your answer. I'll try your suggestion of putting the fields in the subclasses.. I can see how it would work. I currently have a HierarchicalEntity interface and a static 'helper' class with the logic in. Works, but it is not as clean as I would like. I was wondering if @AssociationOverride could be used to redefine the association in some way..Acosta
Unfortunately in this case the issue is with the attribute type. @AssociationOverrides are used to change the Database information but have no attributes to specify the target type.Charcuterie
Hi Again Gordon, I can't find a cleaner solution than what you have suggested for implementing a superclass for the common logic with abstract getters. So you get my vote. BTW. Do you know if this is possible in any other JPA implemetations or is it part of the JPA spec?Acosta
I do not know if any of the other providers support this but I doubt it. The specification does not specifically mention generic attribute types but readers are warned that overriding the mapping meta data is not portable.Charcuterie
K
1

I think I'm late, but people looking for the answer to this problem (like me) should take a look at this: https://bugs.eclipse.org/bugs/show_bug.cgi?id=312132

Klos answered 17/9, 2010 at 17:12 Comment(0)
A
-1

The other provider that supports generic type in a persistent relation is OpenJPA. The assumption OpenJPA makes is that the generic type field is a persistent type and be annotated as such with an (OpenJPA-specific) @Type annotation.

This @Type annotation acts as a placeholder for OpenJPA Mapping engine, and holds a mapping where reference is a persistent identity of the runtime instance. Many years ago, I wrote a blog; I cite it here again not for self-promotion, but hoping it might indicate some pathways for you to support a generic tree without having to push down the concrete type information down a type hierarchy (and thereby losing the essence of generic-based type model).

Antalya answered 13/3, 2015 at 2:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.