Несколько объединений в одну ассоциацию с разными псевдонимами в 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;
Другие вопросы по тегам