2011-01-02 6 views
14

J'essaie d'utiliser JPA 2.0 pour créer des entités polymorphes avec des relations génériques. Il devrait y avoir deux tables, une table d'événements et une table de notification. A l'intérieur les tableau sont des entités concrètes qui sont liés les uns aux autres, comme si:Comment implémenter des entités JPA polymorphes avec des relations génériques

Event <---------- Notification<X extends Event> 
|      | 
LoginEvent <------ LoginNotification extends Notification<LoginEvent> 

Logiquement, cela devrait être possible en veille prolongée, car il est possible dans SQL:

+----------+ +----------+ 
| Event | | Notif | 
+----------+ +----------+ 
|   | | Id  | 
| Id  | <- | Evt_id | 
| Type  | <- | Type  | 
| ...  | | ...  | 
+----------+ +----------+ 

C'est ce que je :

@Entity 
@Inheritance 
public abstract class Event{ 

... 
} 

@Entity 
public class LoginEvent extends Event{ 

... 
} 

@Entity 
@Inheritance 
public abstract class Notification<X extends Event>{ 

@ManyToOne(optional=false, targetEntity=Event.class) 
@JoinColumn 
private X event; 

... 
} 

@Entity 
public class LoginNotification extends Notification<LoginEvent>{ 

... 
} 

en utilisant ce code, je peux persister et chercher tous les cas, la notification, LoginEvent ou NotificationEvent, mais il tombe lorsque je tente d'utiliser la relation LoginNotification_.event dans mes requêtes de métamodèle JPA 2.0. This issue explique quelque chose de similaire.

public static volatile SingularAttribute<NotificationEntity, EventEntity> event; 

Lorsque je tente de faire une jointure dans une requête de critères, je reçois une erreur:

EntityManager em = getEntityManager(); 
CriteriaBuilder cb = em.getCriteriaBuilder(); 
CriteriaQuery<LoginNotification> query = cb.createQuery(LoginNotification.class); 
Root<LoginNotification> root = query.from(LoginNotification.class); 

// This line complains: Type mismatch: cannot convert from 
// Join<LoginNotification,Event> to Join<LoginNotification,LoginEvent> 
Join<LoginNotification, LoginEvent> join = 
root.join(LoginNotification_.event, JoinType.INNER); 

je peux contourner cette erreur, en ajoutant une nouvelle SingularAttribute au LoginNotification_ métamodèle, mais échoue dans l'exécution:

public abstract class LoginNotification_ extends Notification_ { 

    // Adding this Removes Type mismatch error, but causes run-time error 
    public static volatile SingularAttribute<LoginNotification, LoginEvent> event; 

    ... 
} 

Selon certains postes, les relations génériques ne fonctionnera pas (How to handle JPA annotations for a pointer to a generic interface), mais en utilisant un @ManyToOne(optional=false, targetEntity=Event.class) un notation, nous pouvons les amener à se comporter. Malheureusement, les génériques semblent briser la requête des critères JPA.

Existe-t-il des suggestions sur la façon dont je peux effectuer cette recherche? Je peux utiliser LoginNotification.getEvent() dans mon code, mais je ne peux pas utiliser LoginNotification_.event dans mes jointures de métamodèle JPA. Quelle est l'alternative à l'utilisation de génériques pour accomplir cela?

@Pascal Thivent - Pouvez-vous répondre à cette question?

Répondre

8

Une solution est d'éviter d'utiliser la fonction « rejoindre » et faire une croix complète se joindre à la place:

EntityManager em = getEntityManager(); 
CriteriaBuilder cb = em.getCriteriaBuilder(); 
CriteriaQuery<LoginNotification> query = cb.createQuery(LoginNotification.class); 
Root<LoginNotification> notfRoot = query.from(LoginNotification.class); 
Root<LoginEvent> eventRoot = query.from(LoginEvent.class); 
... 
query.where(cb.equals(notfRoot.get(Notification_.event), eventRoot.get(Event_.id)), ...(other criteria)); 

Je suppose qu'un optimiseur de requêtes décent devrait rendre le travail court, mais si quelqu'un a un aperçu de l'efficacité de cette approche, je serais ravi de l'entendre!

+0

Excellent, je l'habitude d'écrire mon rejoint ainsi dans SQL de toute façon. – logan

0

J'ai essayé le code générique, @logan.

Mais j'ai finalement trouvé la façon la plus simple est laissé T implémente Serializable

@Entity 
public class IgsSubject extends BasicObject implements Serializable{ 

    private static final long serialVersionUID = -5387429446192609471L; 
Questions connexes