M:N 未使用 Hibernate 查询获取关联
M:N association not fetched with Hibernate query
我通过 ThreadParticipant
实体在 Thread
和 Participant
之间建立了多对多关系(因为关联有一个附加字段)。我有以下映射。
线程实体
@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
@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 会发疯并执行 four 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
,我的代码根本无法工作,但有了它,我的数据库就会被杀死。我尝试在 Participant
和 ThreadParticipant
之间添加一个 @OneToMany
关联(类似于从 Thread
到 ThreadParticipant
的关联),但结果相同。我还尝试将所有别名添加到查询的字段列表中,但无济于事。
为什么会这样?我的映射或查询有误吗?提前致谢!
尝试将一对多集合更改为 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
。
我通过 ThreadParticipant
实体在 Thread
和 Participant
之间建立了多对多关系(因为关联有一个附加字段)。我有以下映射。
线程实体
@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
@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 会发疯并执行 four 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
,我的代码根本无法工作,但有了它,我的数据库就会被杀死。我尝试在 Participant
和 ThreadParticipant
之间添加一个 @OneToMany
关联(类似于从 Thread
到 ThreadParticipant
的关联),但结果相同。我还尝试将所有别名添加到查询的字段列表中,但无济于事。
为什么会这样?我的映射或查询有误吗?提前致谢!
尝试将一对多集合更改为 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
。