为什么 Hibernate @OneToOne 执行多个 select 查询而不是一个?

Why does Hibernate @OneToOne execute multiple select queries instead of one?

这是我的实体:

@Entity
public class ProductStateEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private Integer id;

    @OneToOne
    @JoinColumn(name = "product_id", nullable = false)
    private ProductEntity product;

    // other fields
}


@Entity
public class ProductEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private Integer id;

    // other fields
}

如果我这样提出请求:

session.get(ProductStateEntity.class, 10);

SQL是这样构成的:

SELECT product_states.id, product_states.product_id, products.id, -- other columns
FROM product_states
INNER JOIN products ON product_states.product_id=products.id
WHERE product_states.id=10

到目前为止,一切顺利,使用 INNER JOIN


如果您通过这种方式提出请求:

session.createQuery("from ProductStateEntity where id = :id")
            .setParameter("id", 10)
            .list()

SQL是这样构成的:

SELECT product_states.id, product_states.product_id, -- other columns
FROM product_states
WHERE product_states.id=10;

SELECT products.id, -- other columns
FROM products
WHERE products.id=10

在这种情况下,提出了 2 个请求。首先在 product_states 中进行查询,然后在产品中进行查询。


这还不算,现在我们来做这样一个请求,一次接收4个id的4条记录:

session.createQuery("from ProductStateEntity where id in :ids")
            .setParameter("ids", Arrays.asList(10, 11, 12, 13))
            .list();

SQL是这样构成的:

SELECT product_states.id, product_states.product_id, -- other columns
FROM product_states
WHERE product_states.id IN (10, 11, 12, 13);

SELECT products.id, -- other columns
FROM products
WHERE products.id=10;

SELECT products.id, -- other columns
FROM products
WHERE products.id=11;

SELECT products.id, -- other columns
FROM products
WHERE products.id=12;

SELECT products.id, -- other columns
FROM products
WHERE products.id=13;

在这种情况下,提出了 5 个请求。首先在product_states中发起一个请求,获取所有商品的id,然后在1个请求中完成,分别接收4个商品。


join fetch添加到上一个查询:

session.createQuery("from ProductStateEntity p join fetch p.product where p.id in :ids")
            .setParameter("ids", Arrays.asList(10, 11, 12, 13))
            .list();

SQL是这样构成的:

SELECT product_states.id, products.id, product_states.product_id, -- other columns
FROM product_states
INNER JOIN products ON product_states.product_id=products.id
WHERE product_states.id IN (10, 11, 12, 13)

因此,INNER JOIN只发出了1个请求,这就是我想要实现的。


所以问题是:

  1. 为什么需要在createQuery中明确指定join fetch?可以做出这种默认行为吗?毕竟,带有 join 的单个查询比很多查询要好。
  2. 为什么在未指定 join fetch 的情况下,其他 select 查询未与 id in (...) 合并为一个查询?相反,Hibernate 使 select 一次一个。这个可以定制吗?

n+1 获取策略是 Hibernate 的默认策略 - 只是因为,如 documentation

中所述

These defaults make sense for most associations in the majority of applications

要全局更改此行为,您可以设置 hibernate.default_batch_fetch_size,并且您会在互联网上找到一些关于 how to set proper value and why

的主题

还有一件事 - 人们普遍认为 fetch join 是所有问题的解决方案,但 不是 。我们必须记住Cartesian Product Problem

获取策略取决于我们的应用程序的工作方式、环境设置(例如数据库连接中的延迟)、我们使用的数据模型以及许多其他因素。没有一个好的解决方案适合所有人,这就是为什么我们在 Hibernate 中有很多抓取策略