You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Entity Graph实体层失效:按钱包所有者ID查CryptoCount出现多查询问题

问题:使用NamedEntityGraph后出现两次数据库查询

实体链与实体类代码

实体链:User -> CryptoWallet -> List<CryptoCount>

1. User类代码

public class User {

    @Id
    @Column(name = "id")
    @GenericGenerator(name = "uuidFromIp" , strategy = "org.hibernate.id.uuid.CustomVersionOneStrategy")
    @GeneratedValue(generator = "uuid_gen_strategy")
    private UUID id;

    @Column(name = "user_name")
    private String userName;

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

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "crypto_wallet_id")
    private CryptoWallet cryptoWallet;
}

2. CryptoWallet类代码

public class CryptoWallet {
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "balance")
    private BigDecimal balance;

    @OneToMany(mappedBy = "cryptoWallet", cascade = CascadeType.ALL)
    private List<CryptoCount> cryptoCounts;

    @OneToOne(mappedBy = "cryptoWallet")
    private User walletOwner;
}

3. CryptoCount类代码

public class CryptoCount {

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

    @ManyToOne
    @JoinColumn(name = "crypto_id")
    private CryptoCurrency cryptoCurrency;

    @Column(name = "count")
    private BigDecimal countOfCrypto;

    @ManyToOne
    @JoinColumn(name = "crypto_wallet_id")
    private CryptoWallet cryptoWallet;
}

需求与两种Repository实现

需求:通过一次数据库查询,根据钱包所有者ID加载所有CryptoCount数据。

方法一(正常,仅一次查询)

@EntityGraph(attributePaths = {"cryptoCurrency","cryptoWallet","cryptoWallet.walletOwner"})
List<CryptoCount> findAllByCryptoWalletWalletOwnerId(UUID id);

方法二(出现两次查询)

先在实体类定义NamedEntityGraph:

  • CryptoCount类上添加:
@NamedEntityGraph(name = "CryptoCountWithCurrencyAndWallet",attributeNodes = {
        @NamedAttributeNode(value = "cryptoCurrency"),
        @NamedAttributeNode(value = "cryptoWallet")
})
  • CryptoWallet类上添加:
@NamedEntityGraph(name = "WalletWithCryptoCountsAndWalletOwner", attributeNodes = {
        @NamedAttributeNode(value = "cryptoCounts"),
        @NamedAttributeNode(value = "walletOwner",subgraph = "UserWithWallet")
}, subgraphs = {
        @NamedSubgraph(name = "UserWithWallet", attributeNodes = @NamedAttributeNode("cryptoWallet"))
})
  • User类上添加:
@NamedEntityGraph(name = "UserWithWallet",attributeNodes = {
        @NamedAttributeNode("cryptoWallet")
},subgraphs = {
        @NamedSubgraph(name = "userWithWallet", attributeNodes = {@NamedAttributeNode("cryptoWallet")})
})

然后Repository方法:

@EntityGraph(value = "CryptoCountWithCurrencyAndWallet")
List<CryptoCount> findAllByCryptoWalletWalletOwnerId(UUID id);

问题:为什么方法二会执行两次数据库查询?


解答

核心问题在于你定义的CryptoCountWithCurrencyAndWallet这个NamedEntityGraph没有包含cryptoWallet.walletOwner的关联加载,而方法一中的attributePaths明确指定了这个嵌套关联。

当使用方法二的NamedEntityGraph时,Hibernate只会加载CryptoCount关联的cryptoCurrency和cryptoWallet,但不会自动加载cryptoWallet关联的walletOwner。而你的查询条件依赖walletOwner的ID,Hibernate为了匹配这个条件,在加载完CryptoCount和cryptoWallet后,会额外发起一次查询去获取walletOwner数据,因此出现了两次查询。

修复方案

修改CryptoCount类上的NamedEntityGraph,通过子图包含cryptoWallet.walletOwner的关联:

@NamedEntityGraph(name = "CryptoCountWithCurrencyAndWalletAndOwner", 
    attributeNodes = {
        @NamedAttributeNode(value = "cryptoCurrency"),
        @NamedAttributeNode(value = "cryptoWallet", subgraph = "WalletWithOwner")
    },
    subgraphs = {
        @NamedSubgraph(name = "WalletWithOwner", 
            attributeNodes = @NamedAttributeNode("walletOwner"))
    })

然后在Repository中使用这个新的NamedEntityGraph:

@EntityGraph(value = "CryptoCountWithCurrencyAndWalletAndOwner")
List<CryptoCount> findAllByCryptoWalletWalletOwnerId(UUID id);

这样Hibernate就会在一次查询中通过左连接加载CryptoCount、cryptoCurrency、cryptoWallet以及walletOwner,避免额外的查询。

另外,你之前在User和CryptoWallet上定义的NamedEntityGraph与当前需求无关,无需使用——当前是从CryptoCount出发加载关联,不需要反向加载的图结构。


内容的提问来源于stack exchange,提问作者Smetana Po Aktsii

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 23:51:37