M: n ассоциация не извлекается с помощью запроса Hibernate
У меня есть отношение "многие ко многим" между Thread
и Participant
через сущность ThreadParticipant
(потому что ассоциация имеет дополнительное поле). У меня есть следующее отображение.
Сущность потока
@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
}
ThreadParticipant entity
@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
}
Теперь я пытаюсь извлечь потоки со следующим запросом (используя Spring Data JPA) и Hibernate как мой JPA поставщик.
@Repository
public interface ThreadRepository extends JpaRepository<Thread, Integer> {
@Query("select distinct t from Thread t inner join fetch t.threadParticipants tp inner join fetch tp.participant p left join fetch p.account a left join fetch p.company c")
public List<Thread> test();
}
Проблема заключается в том, что когда тип выборки для ассоциаций в ThreadParticipants
установлен в FetchType.LAZY
, коллекция Thread.threadParticipants
пуста. Следовательно, если я установлю ассоциации на FetchType.EAGER
, Thread.threadParticipants
содержит два элемента (как и положено). В этом случае, однако, Hibernate сходит с ума и выполняет четыре SQL-запроса для извлечения одного потока.
Hibernate: select thread0_.id as id1_18_0_, threadpart1_.participant_id as particip2_19_1_, threadpart1_.thread_id as thread_i3_19_1_, participan2_.id as id1_12_2_, account3_.id as id1_0_3_, company4_.id as id1_6_4_, thread0_.created as created2_18_0_, thread0_.last_activity as last_act3_18_0_, thread0_.subject as subject4_18_0_, threadpart1_.last_viewed as last_vie1_19_1_, threadpart1_.thread_id as thread_i3_18_0__, threadpart1_.participant_id as particip2_19_0__, threadpart1_.thread_id as thread_i3_19_0__, participan2_.account_id as account_2_12_2_, participan2_.company_id as company_3_12_2_, account3_.email as email2_0_3_, account3_.facebook_profile_id as facebook3_0_3_, account3_.first_name as first_na4_0_3_, account3_.last_name as last_nam5_0_3_, account3_.middle_name as middle_n6_0_3_, company4_.additional_address_text as addition2_6_4_, company4_.banner_name as banner_n3_6_4_, company4_.ci_number as ci_numbe4_6_4_, company4_.city_id as city_id22_6_4_, company4_.co_name as co_name5_6_4_, company4_.company_type_code as company_6_6_4_, company4_.created as created7_6_4_, company4_.description as descript8_6_4_, company4_.email as email9_6_4_, company4_.last_modified as last_mo10_6_4_, company4_.logo_name as logo_na11_6_4_, company4_.name as name12_6_4_, company4_.number_of_reviews as number_13_6_4_, company4_.phone_number as phone_n14_6_4_, company4_.postal_box as postal_15_6_4_, company4_.rating as rating16_6_4_, company4_.second_phone_number as second_17_6_4_, company4_.street_name as street_18_6_4_, company4_.street_number as street_19_6_4_, company4_.teaser as teaser20_6_4_, company4_.website as website21_6_4_ from thread thread0_ inner join thread_participant threadpart1_ on thread0_.id=threadpart1_.thread_id inner join participant participan2_ on threadpart1_.participant_id=participan2_.id left outer join account account3_ on participan2_.account_id=account3_.id left outer join company company4_ on participan2_.company_id=company4_.id
Hibernate: select participan0_.id as id1_12_0_, participan0_.account_id as account_2_12_0_, participan0_.company_id as company_3_12_0_ from participant participan0_ where participan0_.id=?
Hibernate: select thread0_.id as id1_18_0_, thread0_.created as created2_18_0_, thread0_.last_activity as last_act3_18_0_, thread0_.subject as subject4_18_0_ from thread thread0_ where thread0_.id=?
Hibernate: select participan0_.id as id1_12_0_, participan0_.account_id as account_2_12_0_, participan0_.company_id as company_3_12_0_ from participant participan0_ where participan0_.id=?
По-видимому, он выполняет запрос для каждого участника и два запроса для каждого потока. Итак, без FetchType.EAGER
, мой код просто не работает, но с ним моя база данных будет убита. Я попытался добавить ассоциацию @OneToMany
между Participant
и ThreadParticipant
(аналогичную той, что была от Thread
до ThreadParticipant
), но с теми же результатами. Я также попытался добавить все псевдонимы в список полей моего запроса, но безрезультатно.
Почему это происходит? Мое отображение или запрос неверны? Заранее вам спасибо!
1 ответ:
Попробуйте изменить коллекцию The one-to-many на
Set
:@OneToMany(fetch = FetchType.LAZY, mappedBy = "thread", cascade = { CascadeType.PERSIST, CascadeType.MERGE }) private Set<ThreadParticipant> threadParticipants = new HashSet<>();
Дополнительные запросы никогда не должны создаваться, если у вас есть только Ассоциации
LAZY
. Это может быть связано с тем, что отношенияmany-to-one
иone-to-one
по умолчанию являютсяEAGER
. Попробуйте установить вместо них значениеLAZY
.