spring 如何用最少的sql查询加载两个相关实体集合?

ycl3bljg  于 2023-01-12  发布在  Spring
关注(0)|答案(1)|浏览(120)

我有实体:
银行:

public class Bank {

    @Id
    @Column(name = "bank_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private UUID id;

    @Column(name = "bank_name")
    private String bankName;

    @OneToMany(orphanRemoval = true, cascade = CascadeType.ALL,
            mappedBy = "bank", fetch = FetchType.LAZY)
    private List<CreditDetails> creditDetails = new ArrayList<>();

    @OneToMany(orphanRemoval = true, cascade = CascadeType.ALL,
            mappedBy = "bank", fetch = FetchType.LAZY)
    private List<Client> clients = new ArrayList<>();
}

委托单位:

public class Client {

    @Id
    @Column(name = "client_id")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private UUID id;

    @Column(name = "client_name")
    private String clientName;

    @Column(name = "client_surname")
    private String clientSurName;

    @Column(name = "client_full_name")
    private String clientFullName;

    @Column(name = "telephone_number")
    private Long telephoneNumber;

    @Column(name = "email")
    private String email;

    @Column(name = "passport_number")
    private Long passportNumber;

    @ManyToOne(
            cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
    @JoinColumn(name = "bank_id")
    private Bank bank;

    @OneToMany(cascade = CascadeType.ALL,
            mappedBy = "client")
    private List<CreditOffer> creditOffers = new ArrayList<>();

}

信用详情:

public class CreditDetails {

    @Id
    @Column(name = "credit_details_id")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private UUID id;

    @Column(name = "credit_limit")
    private BigDecimal creditLimit;

    @Column(name = "credit_percent")
    private BigDecimal creditPercent;

    @ManyToOne(targetEntity = Bank.class, cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}, fetch = FetchType.EAGER)
    @JoinColumn(name = "bank_id")
    private Bank bank;

    @OneToMany(cascade = CascadeType.ALL,
            mappedBy = "creditDetails")
    List<CreditOffer> creditOffers = new ArrayList<>();
}

我的任务是使用Spring Data以最少的请求加载Clients和Credit Details。
我试图用图来解决这个问题,我在bank实体上面添加了以下结构:

@NamedEntityGraph(name = "BankWithClientsAndDetails", attributeNodes = {
        @NamedAttributeNode(value = "creditDetails"),
        @NamedAttributeNode(value = "clients",subgraph = "ClientWithBank")
})

然而,当调用该方法时,我收到了一个MultyBagFetchException,在Google上搜索到这是由于在请求中获得了笛卡尔积。接下来,我试图通过手动编写一个sql查询来解决这个问题,我做了一个如下形式的测试方法:

@Query(value = "SELECT * FROM banks \n" +
            "JOIN clients ON clients.bank_id = banks.bank_id  \n" +
            "JOIN credit_details ON credit_details.bank_id = banks.bank_id \n" +
            "WHERE banks.bank_id = :id",nativeQuery = true)
    Optional<Bank> findBankWithSubEntitiesById(@Param(value = "id") UUID id);

请求的数量仍然很大,因为关联实体有更多的关联实体,而这些实体又有更多的关联实体。在这个阶段,我的手放下了,我来找你寻求帮助。你会如何解决这种情况?我的情况真的要忍受N + 1吗?我会感激任何帮助

i7uaboj4

i7uaboj41#

在查询中只使用JOIN只会缩小结果范围,例如,它不会加载没有任何客户和信用详细信息的银行。
您可以使用JOIN FETCH jpql关键字告诉Hibernate急切地加载这些关联中的每一个。

SELECT bank
FROM Bank bank
JOIN FETCH bank.clients
JOIN FETCH bank.creditDetails
WHERE bank.id = :bankId

请记住,这是一个jpql查询,因此您必须删除nativeQuery = true参数。另外,请注意,默认情况下,join是内部连接而不是外部连接-这意味着此查询也不会返回没有客户或信用详细信息的银行。您可以使用LEFT JOIN FETCH更改此设置,但这会降低性能,因此请选择最适合您的用例的策略。
Further reading

相关问题