I'm trying to use JPA 2.0 to create polymorphic entities with generic relations. There should be two tables, an event table and a notification table. Inside those table are concrete entities that are related to one another, like so:
Event
| |
LoginEvent
Logically this should be possible in hibernate, as it is possible in SQL:
+----------+ +----------+
| Event | | Notif |
+----------+ +----------+
| | | Id |
| Id |
| Type |
| ... | | ... |
+----------+ +----------+
This is what I have:
@Entity
@Inheritance
public abstract class Event{
...
}
@Entity
public class LoginEvent extends Event{
...
}
@Entity
@Inheritance
public abstract class Notification{
@ManyToOne(optional=false, targetEntity=Event.class)
@JoinColumn
private X event;
...
}
@Entity
public class LoginNotification extends Notification{
...
}
Using this code, I can persist and fetch any Event, Notification, LoginEvent, or NotificationEvent, but it falls down when I try to use the LoginNotification_.event relation in my JPA 2.0 metamodel queries. This issue explains something similar.
public static volatile SingularAttribute event;
When I try to do a join in a criteria query, I get an error:
EntityManager em = getEntityManager();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery query = cb.createQuery(LoginNotification.class);
Root root = query.from(LoginNotification.class);
// This line complains: Type mismatch: cannot convert from
// Join to Join
Join join =
root.join(LoginNotification_.event, JoinType.INNER);
I can get around this error, by adding a new SingularAttribute to the LoginNotification_ metamodel, but this fails in execution:
public abstract class LoginNotification_ extends Notification_ {
// Adding this Removes Type mismatch error, but causes run-time error
public static volatile SingularAttribute event;
...
}
According to some posts, generic relations won't work (How to handle JPA annotations for a pointer to a generic interface), but by using a @ManyToOne(optional=false, targetEntity=Event.class) annotation, we can get them to behave. Unfortunately, the generics seem to break the JPA criteria query.
Are there any suggestions on how I can perform this lookup? I can use LoginNotification.getEvent() in my code, but I cannot use LoginNotification_.event in my JPA metamodel joins. What's the alternative to using generics to accomplish this?
@Pascal Thivent - Can you answer this?
解决方案
One solution to this is to avoid using the 'join' function and do a full cross join instead:
EntityManager em = getEntityManager();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery query = cb.createQuery(LoginNotification.class);
Root notfRoot = query.from(LoginNotification.class);
Root eventRoot = query.from(LoginEvent.class);
...
query.where(cb.equals(notfRoot.get(Notification_.event), eventRoot.get(Event_.id)), ...(other criteria));
I would assume that a decent query optimizer should make short work of this, but if anyone has any insight on the efficiency of this approach I would be keen to hear it!