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 a开发者_开发百科ttribute [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.
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.
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
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).
精彩评论