2010-08-08 5 views
5

Ho alcune classi di modelli di dominio nella mia app Web che hanno una relazione gerarchica con se stessi. Un esempio di uno è la struttura gerarchica delle categorie utilizzata per classificare i messaggi degli utenti.EclipseLink @MappedSuperclass e generici

C'è una logica relativa alla natura gerarchica di queste classi che è comune. Così ho provato a spostare la logica in una generica superclasse annotata @MappedSuperclass.

Qualcosa di simile:

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

Le sottoclassi poi si estendono HierarchicalBaseEntity dando se stessi come il tipo generico N:

@Entity 
public class CategoryBean extends HierarchicalBaseEntity<CategoryBean> { 

In Java questo funziona tutto molto pulito. Ma purtroppo EclipseLink non sembra piacere il 'genitore' campo generico:

private N parent; 

Dà la seguente eccezione:

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) 

Perché è lamentano una stringa non-entità?

Come test ho provato a rimuovere i farmaci generici e solo per avere il campo genitore definito come:

private HierarchicalBaseEntity parent; 

Senza farmaci generici, EclipseLink ha dato questa eccezione:

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) 

vero HierarchicalBaseEntity suo non è un Entity in entrambi i casi, è @MappedSuperclass .. ma c'è un modo per farlo con i generici o in altro modo? Sembra che tu non possa avere un campo nella tua @MappedSuperclass che fa riferimento a una delle sue sottoclassi.

+0

Questo sembra in qualche modo * simile * a http://forums.sun.com/thread.jspa?threadID=5268944 (ma il problema correlato è stato risolto). Potresti provare con un altro fornitore? –

risposta

3

Il problema è che quando si utilizza Generics come tipi di campo per le relazioni, EclipseLink non può sapere quale sia il tipo di destinazione fino al runtime quando l'istanza effettiva viene ispezionata. Quindi la mappatura dovrebbe essere creata dinamicamente in fase di esecuzione e questo non è supportato.

È possibile continuare a utilizzare la SuperClass generica, ma ciò richiederebbe lo spostamento del campo verso le Entità in cui avrebbero definito i tipi, quindi hanno getter/setter interni astratti per quei campi che restituiscono Object che i metodi generici chiamerebbero il casting per tipo generico. Convulsa ma consentirebbe la Generic MappedSuperclass.

+0

Hi Gordon, Grazie per la risposta. Proverò il tuo suggerimento di inserire i campi nelle sottoclassi .. Vedo come funzionerebbe. Attualmente ho un'interfaccia HierarchicalEntity e una classe 'helper' statica con la logica in. Funziona, ma non è pulita come vorrei. Mi chiedevo se @AssociationOverride potesse essere usato per ridefinire l'associazione in qualche modo .. –

+0

Sfortunatamente in questo caso il problema è con il tipo di attributo. @AssociationOverrides vengono utilizzati per modificare le informazioni del database ma non hanno attributi per specificare il tipo di destinazione. –

+0

Ciao ancora Gordon, non riesco a trovare una soluzione più pulita di quella che hai suggerito per l'implementazione di una superclasse per la logica comune con i getter astratti. Quindi ottieni il mio voto. BTW. Sai se questo è possibile in altre implementazioni JPA o fa parte delle specifiche JPA? –

-1

L'altro fornitore che supporta tipo generico in una relazione permanente è OpenJPA . L'assunto di OpenJPA è che il campo del tipo generico è un tipo persistente e viene annotato come tale con un'annotazione @Type (specifica per OpenJPA).

Questa annotazione @Type funge da segnaposto per il motore di mappatura OpenJPA e contiene una mappatura in cui il riferimento è un'identità persistente dell'istanza di runtime. Molti anni fa, ho scritto un blog; Cito di nuovo qui non per auto-promozione, ma sperando che possa indicare alcuni percorsi per supportare un albero generico senza dover spingere verso il basso le informazioni di tipo concreto su una gerarchia di tipi (e quindi perdere l'essenza del modello di tipo generico).