为什么 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个请求,这就是我想要实现的。
所以问题是:
- 为什么需要在
createQuery
中明确指定join fetch
?可以做出这种默认行为吗?毕竟,带有 join 的单个查询比很多查询要好。
- 为什么在未指定
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 中有很多抓取策略
这是我的实体:
@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个请求,这就是我想要实现的。
所以问题是:
- 为什么需要在
createQuery
中明确指定join fetch
?可以做出这种默认行为吗?毕竟,带有 join 的单个查询比很多查询要好。 - 为什么在未指定
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 中有很多抓取策略