2010-08-08 16 views
5

Tengo algunas clases de modelo de dominio en mi aplicación web que tienen una relación jerárquica con ellas mismas. Un ejemplo de uno es la estructura jerárquica de categorías utilizada para clasificar las publicaciones de los usuarios.EclipseLink @MappedSuperclass y generics

Existe cierta lógica relacionada con la naturaleza jerárquica de estas clases que es común. Así que traté de mover la lógica a una superclase genérica @MappedSuperclass anotada.

Algo así como:

@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) { ... 
} 

Las subclases se extienden entonces HierarchicalBaseEntity dando a sí mismos como el tipo genérico N:

@Entity 
public class CategoryBean extends HierarchicalBaseEntity<CategoryBean> { 

En Java esto todo funciona bastante limpia. Pero, por desgracia EclipseLink no parece que le gusta el campo genérico de 'padre':

private N parent; 

Se da la siguiente excepción:

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) 

¿Por qué se quejan de una cadena no entidad?

Como prueba de que he intentado quitar los genéricos y sólo tener el campo primario define como:

private HierarchicalBaseEntity parent; 

Sin genéricos, EclipseLink dio esta excepción:

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) 

verdadera HierarchicalBaseEntity no es una Entity en cualquier caso, es una @MappedSuperclass ... pero ¿hay alguna manera de hacer esto con genéricos u otros? Parece que no puedes tener un campo en tu @MappedSuperclass que haga referencia a una de sus subclases.

+0

Esto se ve de alguna manera * similar * a http://forums.sun.com/thread.jspa?threadID=5268944 (pero el problema relacionado es fijo). ¿Podrías probar con otro proveedor? –

Respuesta

3

El problema es que cuando se utilizan genéricos como tipos de campo para las relaciones, EclipseLink no puede saber cuál es el tipo de destino hasta el momento de la ejecución cuando se inspecciona la instancia real. Por lo tanto, la asignación tendría que crearse dinámicamente en tiempo de ejecución y esto no es compatible.

Podría seguir utilizando la Superclase genérica, pero esto requeriría mover el campo a las entidades donde tendrían tipos definidos y luego tener getters/setters internos abstractos para los campos que devuelven Object que los métodos genéricos llamarían casting al tipo genérico. Enrevesado, pero permitiría el genérico MappedSuperclass.

+0

Hola Gordon, Gracias por su respuesta. Intentaré su sugerencia de poner los campos en las subclases ... Puedo ver cómo funcionaría. Actualmente tengo una interfaz HierarchicalEntity y una clase estática de 'ayuda' con la lógica en. Funciona, pero no es tan limpio como me gustaría. Me preguntaba si @AssociationOverride podría usarse para redefinir la asociación de alguna manera. –

+0

Desafortunadamente, en este caso el problema es con el tipo de atributo. @AssociationOverrides se usa para cambiar la información de la base de datos pero no tiene atributos para especificar el tipo de destino. –

+0

Hola otra vez Gordon, no puedo encontrar una solución más limpia que la sugerida para implementar una superclase para la lógica común con getters abstractos. Entonces obtienes mi voto. Por cierto. ¿Sabes si esto es posible en otras implementaciones de JPA o es parte de la especificación de JPA? –

-1

El otro proveedor que soporta tipo genérico en una relación persistente es OpenJPA . La suposición que hace OpenJPA es que el campo de tipo genérico es de tipo persistente y se anota como tal con una anotación @Type (específica de OpenJPA).

Esta anotación @Type actúa como un marcador de posición para el motor de asignación de OpenJPA y contiene una asignación donde la referencia es una identidad persistente de la instancia de tiempo de ejecución. Hace muchos años, escribí un blog; Lo cito aquí otra vez no para autopromoción, pero espero que pueda indicar algunas vías para que pueda soportar un árbol genérico sin tener que bajar la información de tipo concreto en una jerarquía de tipos (y perder así la esencia del modelo de tipo genérico)