Несколько объединений в одну ассоциацию с разными псевдонимами в Hibernate
Я создаю систему обмена сообщениями для своего веб-приложения, используя Spring MVC с Spring Data JPA и Hibernate в качестве поставщика JPA.
У меня есть пять лиц: Thread
, ThreadParticipant
, Participant
, Account
а также Company
, Каждая ветка сообщений имеет как минимум двух участников, один из которых связан с пользователем (Account
лицо), а другой связан с Company
, Это ограничение применяется приложением. База данных разработана таким образом, чтобы поддерживать будущие функции. Пример двух участников для данного потока в базе данных выглядит следующим образом:
id account_id company_id
1 44 NULL
2 NULL 123
Строка с id=1 - это пользователь, а строка с id=2 - это компания. Что я хочу сделать, это написать HQL-запрос, который извлекает все Thread
объекты для данной учетной записи, содержащие как пользователя / участника учетной записи, так и участника компании. Я пытался использовать разные псевдонимы для моих объединений, например:
select distinct t
from Thread t
inner join fetch t.threadParticipants user_tp
inner join fetch t.threadParticipants company_tp
inner join fetch user_tp.participant user_p
inner join fetch user_p.account a
inner join fetch company_tp.participant receiver_p
inner join fetch receiver_p.company
where a.id = :accountId
Я получаю исключение cannot simultaneously fetch multiple bags
из-за двух плодов t.threadParticipants
, Если я сделаю только одно соединение, сгенерированный SQL просто проигнорирует мое дополнительное соединение и только присоединится к Participant
один раз, который требует, чтобы участник имел как учетную запись, так и компанию, связанную. С сырым SQL я могу сделать так, и он работает нормально:
select *
from thread t
inner join thread_participant user_tp on (user_tp.thread_id = t.id)
inner join thread_participant company_tp on (company_tp.thread_id = t.id)
inner join participant user_p on (user_p.id = user_tp.participant_id)
inner join account a on (a.id = user_p.account_id)
inner join participant company_p on (company_p.id = company_tp.participant_id)
inner join company c on (c.id = company_p.company_id)
where a.id = 123;
Если я не использую разные псевдонимы для одной и той же таблицы (см. Запрос ниже), запрос выполняется нормально, но я получаю только одного из участников потока - тот, который связан с учетной записью.
select distinct t
from Thread t
inner join fetch t.threadParticipants tp
inner join fetch tp.participant p
inner join fetch p.account a
left join fetch p.company
where a.id = :accountId
Есть ли способ сделать то, что я пытаюсь сделать с HQL, или я должен идти с использованием нативного SQL?
Мое сопоставление выглядит следующим образом:
Поток сущности
@Entity
@Table(name = "thread")
public class Thread {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column
private int id;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "thread", cascade = { CascadeType.PERSIST, CascadeType.MERGE })
private Collection<ThreadParticipant> threadParticipants = new HashSet<>();
// Getters and setters
}
Участник
@Entity
@Table(name = "participant")
public class Participant {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column
private int id;
@ManyToOne(fetch = FetchType.LAZY, optional = true, targetEntity = Account.class, cascade = { CascadeType.PERSIST })
@JoinColumn(name = "account_id")
private Account account;
@ManyToOne(fetch = FetchType.LAZY, optional = true, targetEntity = Company.class)
@JoinColumn(name = "company_id")
private Company company;
// Getters and setters
}
ТемаУчастник
@Entity
@Table(name = "thread_participant")
@IdClass(ThreadParticipantPK.class)
public class ThreadParticipant implements Serializable {
@Id
@ManyToOne(fetch = FetchType.LAZY, targetEntity = Participant.class, cascade = { CascadeType.PERSIST, CascadeType.MERGE })
@JoinColumn(name = "participant_id")
private Participant participant;
@Id
@ManyToOne(fetch = FetchType.LAZY, targetEntity = Thread.class)
@JoinColumn(name = "thread_id")
private Thread thread;
@Column(name = "last_viewed", nullable = true)
private Date lastViewed;
// Getters and setters
}
ThreadParticipantPK
public class ThreadParticipantPK implements Serializable {
private Thread thread;
private Participant participant;
public ThreadParticipantPK() { }
public ThreadParticipantPK(Thread thread, Participant participant) {
this.thread = thread;
this.participant = participant;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ThreadParticipantPK)) return false;
ThreadParticipantPK that = (ThreadParticipantPK) o;
if (!participant.equals(that.participant)) return false;
if (!thread.equals(that.thread)) return false;
return true;
}
@Override
public int hashCode() {
int result = thread.hashCode();
result = 31 * result + participant.hashCode();
return result;
}
// Getters and setters
}
Заранее спасибо!
1 ответ
Попробуйте изменить тип threadParticipants
сбор в Set
вместо Collection
:
private Set<ThreadParticipant> threadParticipants;