Как реализовать полиморфные сущности JPA с родовыми отношениями - PullRequest
15 голосов
/ 03 января 2011

Я пытаюсь использовать 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>{

...
}

Используя этот код, я могу сохранить и извлечь любое событие, уведомление, 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 - Вы можете ответить на этот вопрос?

Ответы [ 2 ]

8 голосов
/ 03 января 2011

Одно из решений этой проблемы - избегать использования функции '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));

Я бы предположил, что приличный оптимизатор запросов должен справиться с этой задачей, но если кто-то и поймет, насколько эффективен этот подход, я бы хотел услышать это!

0 голосов
/ 14 декабря 2015

Я пробовал код для вас, @logan.

Но я, наконец, нашел самый простой способ - пусть T реализует Serializable

@Entity
public class IgsSubject extends BasicObject implements Serializable{

    private static final long serialVersionUID = -5387429446192609471L;
...