Как реализовать полиморфные объекты JPA с родовыми отношениями
Я пытаюсь использовать JPA 2.0 для создания полиморфных объектов с родовыми отношениями. Должны быть две таблицы, таблица событий и таблица уведомлений. Внутри этих таблиц есть конкретные сущности, которые связаны друг с другом, например:
Event <---------- Notification<X extends Event>
| |
LoginEvent <------ LoginNotification extends Notification<LoginEvent>
Логически это должно быть возможно в спящем режиме, как это возможно в SQL:
+----------+ +----------+
| Event | | Notif |
+----------+ +----------+
| | | Id |
| Id | <- | Evt_id |
| Type | <- | Type |
| ... | | ... |
+----------+ +----------+
Это то, что у меня есть:
@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>{
...
}
Используя этот код, я могу сохранять и извлекать любые события, уведомления, входные данные или NotificationEvent, но он падает, когда я пытаюсь использовать отношение LoginNotification_.event
в моих метамодельных запросах JPA 2.0. Эта проблема объясняет нечто подобное.
public static volatile SingularAttribute<NotificationEntity, EventEntity> event;
Когда я пытаюсь выполнить соединение в запросе критерия, я получаю сообщение об ошибке:
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);
Я могу обойти эту ошибку, добавив новую SingularAttribute
в метамодель LoginNotification_
, но это не выполняется:
public abstract class LoginNotification_ extends Notification_ {
// Adding this Removes Type mismatch error, but causes run-time error
public static volatile SingularAttribute<LoginNotification, LoginEvent> event;
...
}
По некоторым сообщениям, общие отношения не будут работать (Как обрабатывать аннотации JPA для указателя на общий интерфейс), но используя @ManyToOne(optional=false, targetEntity=Event.class)
аннотация, мы можем заставить их вести себя. К сожалению, дженерики, похоже, нарушают запрос критериев JPA.
Есть ли какие-либо предложения о том, как я могу выполнить этот поиск? Я могу использовать LoginNotification.getEvent()
в своем коде, но я не могу использовать LoginNotification_.event
в моих метамодели JPA. Какая альтернатива использованию дженериков для этого?
@Pascal Thivent - Можете ли вы ответить на это?
Ответы
Ответ 1
Одно из решений заключается в том, чтобы избежать использования функции "join" и вместо этого выполнить полное перекрестное соединение:
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));
Я бы предположил, что достойный оптимизатор запросов должен сделать короткую работу, но если у кого-то есть представление об эффективности этого подхода, я бы очень хотел его услышать!
Ответ 2
Я пробовал ваш код для generic, @logan.
Но, наконец, я нашел самый простой способ: пусть T
реализует Serializable
@Entity
public class IgsSubject extends BasicObject implements Serializable{
private static final long serialVersionUID = -5387429446192609471L;