【问题标题】:M:N association not fetched with Hibernate query未使用 Hibernate 查询获取 M:N 关联
【发布时间】:2023-03-26 06:12:01
【问题描述】:

我通过ThreadParticipant 实体在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
}

线程参与者实体

@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.EAGERThread.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,我的代码根本无法工作,但有了它,我的数据库就会被杀死。我尝试在ParticipantThreadParticipant 之间添加@OneToMany 关联(类似于从ThreadThreadParticipant 的关联),但结果相同。我还尝试将所有别名添加到查询的字段列表中,但无济于事。

为什么会这样?我的映射或查询是否错误?提前谢谢!

【问题讨论】:

    标签: java hibernate jpa orm spring-data-jpa


    【解决方案1】:

    尝试将一对多集合更改为Set

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "thread", cascade = { CascadeType.PERSIST, CascadeType.MERGE })
    private Set<ThreadParticipant> threadParticipants = new HashSet<>();
    

    如果您只有LAZY 关联,则永远不应生成其他查询。这可能是因为many-to-oneone-to-one 关系默认为EAGER。请尝试将它们设置为 LAZY

    【讨论】:

    • 不幸的是,这不会改变 Hibernate 生成的查询数量。
    • 使用 EAGER,您将始终获得那些不必要的查询,请尝试使用 LAZY 和 FETCH 指令。
    • 即使所有关联都与FetchType.LAZY 关联,仍然会生成查询。 Hibernate 正在根据我的查询获取正确的数据,但这样做的策略很奇怪。
    • 如果您在查询中使用 FETCH,则不应获得辅助选择。
    • 我同意,这就是为什么下面的查询会为每个线程生成 四个 SQL 查询如此奇怪的原因(假设每个线程有两个参与者):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跨度>
    猜你喜欢
    • 2017-02-26
    • 2014-09-29
    • 1970-01-01
    • 2011-01-26
    • 1970-01-01
    • 2021-11-22
    • 1970-01-01
    • 2015-09-27
    • 1970-01-01
    相关资源
    最近更新 更多