2014-05-02 3 views
3

Avoir une classe abstraite NodeBase et plusieurs classes s'étendant (NodeAnnounce, NodeTransfer, ...), je voudrais appliquer l'héritage avec l'annotation de JPA @Inheritance et @DiscriminatorColumn .JPA « L'identifiant plusieurs parties ... ne pouvait pas être lié »

Voici mes cours (je condensé un peu, ce n'est pas mon style mais accesseurs sont évidents quand même):

@Entity 
@Inheritance(strategy=InheritanceType.JOINED) 
@DiscriminatorColumn(name="nodeType") 
@Table(name="node_base") 
@NamedQueries({ 
    @NamedQuery(name="NodeBase.findAll", query="SELECT n FROM NodeBase n"), 
    @NamedQuery(name="NodeBase.findByTarget", query="SELECT n FROM NodeBase n JOIN Target t ON t.firstNode = n.id WHERE t.id = :targetID") 
}) 
public abstract class NodeBase implements Serializable { 
    private static final long serialVersionUID = 1L; 
    @Id 
    private int id; 
    private int customer; 
    private String exitNames; 
    private int nodeType; 
    private int diagramPosX; 
    private int diagramPosY; 

    public NodeBase() {} 

    public int getId() { return this.id; } 
    public void setId(int id) { this.id = id; } 

    public int getCustomer() { return this.customer; } 
    public void setCustomer(int customer) { this.customer = customer;} 

    public String getExitNames() { return this.exitNames; } 
    public void setExitNames(String exitNames) { this.exitNames = exitNames; } 

    public int getDiagramPosX() { return diagramPosX; } 
    public void setDiagramPosX(int diagramPosX) { this.diagramPosX = diagramPosX; } 

    public int getDiagramPosY() { return diagramPosY; } 
    public void setDiagramPosY(int diagramPosY) { this.diagramPosY = diagramPosY; } 

    public int getNodeType() { return nodeType; } 
    public void setNodeType(int nodeType) { this.nodeType = nodeType; } 
} 

Une classe héritant (seulement un exemple)

@Entity 
@Table(name="node_announce") 
@DiscriminatorValue(value="2") 
@NamedQuery(name="NodeAnnounce.findAll", query="SELECT n FROM NodeAnnounce n") 
public class NodeAnnounce extends NodeBase implements Serializable { 
    private static final long serialVersionUID = 1L; 

    private String wavefile; 

    public NodeAnnounce() {} 

    public String getWavefile() { return this.wavefile; } 
    public void setWavefile(String wavefile) { this.wavefile = wavefile; } 
} 

Je suis maintenant appeler la requête nommée précédemment définie

NodeBase nodeBase = 
(NodeBase) em.createNamedQuery("NodeBase.findByTarget") 
    .setParameter("targetID", target.getId()) 
    .getSingleResult(); 

Je peux assurer target n'est pas nul et l'identifiant qu'il renvoie est existant. Dans l'appel de fonction ci-dessus, est jeté une exception assez longue:

[EL Warning]: 2014-05-02 18:39:31.287--UnitOfWork(138297553)--Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.0.v20130507-3faac2b): org.eclipse.persistence.exceptions.DatabaseException 
Internal Exception: com.microsoft.sqlserver.jdbc.SQLServerException: The multi-part identifier "node_base.nodeType" could not be bound. 
Error Code: 4104 
Call: SELECT DISTINCT node_base.nodeType FROM target t0, node_base t1 WHERE ((t0.ID = ?) AND (t0.FIRSTNODE = t1.ID)) 
    bind => [1 parameter bound] 
Query: ReadObjectQuery(name="NodeBase.findByTarget" referenceClass=NodeBase sql="SELECT DISTINCT node_base.nodeType FROM target t0, node_base t1 WHERE ((t0.ID = ?) AND (t0.FIRSTNODE = t1.ID))") 
    at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:340) 
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:679) 
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeCall(DatabaseAccessor.java:558) 
    (...) 
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: The multi-part identifier "node_base.nodeType" could not be bound. 
    at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDatabaseError(SQLServerException.java:216) 
    at com.microsoft.sqlserver.jdbc.SQLServerStatement.getNextResult(SQLServerStatement.java:1515) 
    (...) 

Cette ligne de la trace de la pile semble étrange pour moi:

SELECT DISTINCT node_base.nodeType FROM target t0, node_base t1 WHERE ((t0.ID = ?) AND (t0.FIRSTNODE = t1.ID)) 

Pourquoi t0.ID = ?? Ça ne devrait pas. Je sais, c'est encore une autre question de The multi-part identifier. Mais je pense que c'est lié à JPA. Le champ nodeType dans la base de données a une relation avec la table node_type - que je n'utilise pas dans mon projet Java. Mais la valeur du champ est importante de toute façon.

Le serveur principal est SQL Server. Voici la définition de table pertinente:

CREATE TABLE [dbo].[node_base](
    [id] [int] IDENTITY(1,1) NOT NULL, 
    [nodeType] [int] NOT NULL, 
    [exitNames] [varchar](20) NOT NULL, 
    [customer] [int] NOT NULL, 
    [diagramPosX] [int] NULL, 
    [diagramPosY] [int] NULL, 
CONSTRAINT [PK_node_base] PRIMARY KEY CLUSTERED 
(
    [id] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 
+0

Ce est juste un coup dans le noir, mais peut-être votre requête findByTarget devrait commencer comme 'SELECT OBJECT (n) FROM NodeBase n'. Et 't0.ID =?' Est simplement le SQL généré pour le paramètre: targetID. –

+0

Pourriez-vous également montrer la partie de la cible qui se rapporte à la base du nœud? Et donnez également la version d'EclipseLink que vous utilisez. – lpiepiora

Répondre

2

Comme mentionné dans le commentaire, le problème dans votre requête est pas avec le t0.ID = ? comme cela est attendu de la JPQL SELECT n FROM NodeBase n JOIN Target t ON t.firstNode = n.id WHERE t.id = :targetID. Le problème est que lors de l'interrogation sur NodeBase, EclipseLink vérifie d'abord toutes les sous-classes à renvoyer de la requête et donc ne sélectionne que DiscriminatorColumn, "nodeType". Cela renverrait toutes les sous-classes qui doivent être créées en raison de l'héritage, qu'EclipseLink utiliserait ensuite pour les requêtes suivantes.
Malheureusement, vous rencontrez le bug EclipseLink https://bugs.eclipse.org/bugs/show_bug.cgi?id=344721 avec l'héritage. Pour contourner le problème, EclipseLink doit utiliser une stratégie de périphérie externe afin d'importer des sous-classes plutôt que des requêtes séparées. Cela permet tout à apporter à l'aide d'une seule requête, et peut être configuré en utilisant un customizer comme décrit ici: https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Entities/Inheritance#Outer_Joining_Subclasses

Les avantages et les inconvénients d'utiliser une requête vs plusieurs requêtes de l'héritage sont discutés ici: http://en.wikibooks.org/wiki/Java_Persistence/Inheritance

Questions connexes