如何使用旧的休眠标准进行批处理?
How to get batching using the old hibernate criteria?
我仍在使用旧的 org.hibernate.Criteria
并且对获取模式越来越困惑。在各种查询中,我需要以下所有变体,所以我无法通过注释来控制它。我只是将所有内容都切换到 @ManyToOne(fetch=FetchType.LAZY)
,否则,查询中的任何内容都不会更改。
到目前为止,我能找到的要么是 HQL 要么是 JPA2,或者只提供两种选择,但我需要它来满足旧标准和(至少)以下三种情况:
- 执行 JOIN,并从两个 table 中获取。这是可以的,除非数据过于冗余(例如,主数据很大或在结果中重复多次)。在 SQL 中,我会写
SELECT * FROM item JOIN order on item.order_id = order.id
WHERE ...;
- 做一个JOIN,从第一个获取table,然后从另一个分离。这通常是前一个查询的更有效变体。在 SQL 中,我会写
SELECT item.* FROM item JOIN order on item.order_id = order.id
WHERE ...;
SELECT order.* FROM order WHERE ...;
- 执行 JOIN,但不获取已加入的 table。这很有用,例如,用于根据另一个 table 的数据进行排序。在 SQL 中,我会写
SELECT item.* FROM item JOIN order on item.order_id = order.id
WHERE ...
ORDER BY order.name, item.name;
看起来没有明确指定 fetch=FetchType.LAZY
,一切都像第一种情况一样被急切地获取,这有时太糟糕了。我想,使用 Criteria#setFetchMode
,我可以得到第三种情况。我还没有尝试过,因为我仍然想念第二种情况。我知道这是 某种程度上 可能的,因为有 @BatchSize
注释。
- 我说的对吗?
- 有没有办法用旧标准得到第二种情况?
更新
看起来使用 createAlias()
会导致急切地获取所有内容。有一些重载允许指定 JoinType
,但我需要指定获取类型。现在,我更糊涂了。
是的,您可以使用 FetchType.LAZY、BatchSize、不同的获取模式和 projections 来满足所有三种情况(注意我刚刚用 [=18= 组成了一个 'where' 子句] 以确保我检索到很多行):
执行 JOIN,并从两个 table 中获取。
因为一个项目的顺序是FetchType.LAZY,the default fetch mode will be 'SELECT'所以它只需要设置为'JOIN'来从连接而不是单独查询中获取相关的实体数据:
Session session = entityManager.unwrap(org.hibernate.Session.class);
Criteria cr = session.createCriteria(Item.class);
cr.add(Restrictions.like("name", "%s%"));
cr.setFetchMode("order", FetchMode.JOIN);
List results = cr.list();
results.forEach(r -> System.out.println(((Item)r).getOrder().getName()));
生成的单个 SQL 查询:
select
this_.id as id1_0_1_,
this_.name as name2_0_1_,
this_.order_id as order_id3_0_1_,
order2_.id as id1_1_0_,
order2_.name as name2_1_0_
from
item_table this_
left outer join
order_table order2_
on this_.order_id=order2_.id
where
this_.name like ?
做一个 JOIN,从第一个 table 和另一个分开获取。
保留默认的获取模式 'SELECT',为订单创建一个别名以在排序中使用它的列,并使用 projection 到 select 所需的列子集包括外键:
Session session = entityManager.unwrap(org.hibernate.Session.class);
Criteria cr = session.createCriteria(Item.class);
cr.add(Restrictions.like("name", "%s%"));
cr.createAlias("order", "o");
cr.addOrder(org.hibernate.criterion.Order.asc("o.id"));
cr.setProjection(Projections.projectionList()
.add(Projections.property("id"), "id")
.add(Projections.property("name"), "name")
.add(Projections.property("order"), "order"))
.setResultTransformer(org.hibernate.transform.Transformers.aliasToBean(Item.class));
List results = cr.list();
results.forEach(r -> System.out.println(((Item)r).getOrder().getName()));
结果第一个 SQL 查询:
select
this_.id as y0_,
this_.name as y1_,
this_.order_id as y2_
from
item_table this_
inner join
order_table o1_
on this_.order_id=o1_.id
where
this_.name like ?
order by
o1_.id asc
及后续批次(注意我在订单 class 上使用了 @BatchSize(value=5)
):
select
order0_.id as id1_1_0_,
order0_.name as name2_1_0_
from
order_table order0_
where
order0_.id in (
?, ?, ?, ?, ?
)
执行 JOIN,但不获取已加入的 table。
与上例相同,但不做任何提示加载延迟加载订单的操作:
Session session = entityManager.unwrap(org.hibernate.Session.class);
Criteria cr = session.createCriteria(Item.class);
cr.add(Restrictions.like("name", "%s%"));
cr.createAlias("order", "o");
cr.addOrder(Order.asc("o.id"));
cr.setProjection(Projections.projectionList()
.add(Projections.property("id"), "id")
.add(Projections.property("name"), "name")
.add(Projections.property("order"), "order"))
.setResultTransformer(org.hibernate.transform.Transformers.aliasToBean(Item.class));
List results = cr.list();
results.forEach(r -> System.out.println(((Item)r).getName()));
生成的单个 SQL 查询:
select
this_.id as y0_,
this_.name as y1_,
this_.order_id as y2_
from
item_table this_
inner join
order_table o1_
on this_.order_id=o1_.id
where
this_.name like ?
order by
o1_.id asc
我在所有情况下的实体都保持不变:
@Entity
@Table(name = "item_table")
public class Item {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY)
private Order order;
// getters and setters omitted
}
@Entity
@Table(name = "order_table")
@BatchSize(size = 5)
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// getters and setters omitted
}
我仍在使用旧的 org.hibernate.Criteria
并且对获取模式越来越困惑。在各种查询中,我需要以下所有变体,所以我无法通过注释来控制它。我只是将所有内容都切换到 @ManyToOne(fetch=FetchType.LAZY)
,否则,查询中的任何内容都不会更改。
到目前为止,我能找到的要么是 HQL 要么是 JPA2,或者只提供两种选择,但我需要它来满足旧标准和(至少)以下三种情况:
- 执行 JOIN,并从两个 table 中获取。这是可以的,除非数据过于冗余(例如,主数据很大或在结果中重复多次)。在 SQL 中,我会写
SELECT * FROM item JOIN order on item.order_id = order.id
WHERE ...;
- 做一个JOIN,从第一个获取table,然后从另一个分离。这通常是前一个查询的更有效变体。在 SQL 中,我会写
SELECT item.* FROM item JOIN order on item.order_id = order.id
WHERE ...;
SELECT order.* FROM order WHERE ...;
- 执行 JOIN,但不获取已加入的 table。这很有用,例如,用于根据另一个 table 的数据进行排序。在 SQL 中,我会写
SELECT item.* FROM item JOIN order on item.order_id = order.id
WHERE ...
ORDER BY order.name, item.name;
看起来没有明确指定 fetch=FetchType.LAZY
,一切都像第一种情况一样被急切地获取,这有时太糟糕了。我想,使用 Criteria#setFetchMode
,我可以得到第三种情况。我还没有尝试过,因为我仍然想念第二种情况。我知道这是 某种程度上 可能的,因为有 @BatchSize
注释。
- 我说的对吗?
- 有没有办法用旧标准得到第二种情况?
更新
看起来使用 createAlias()
会导致急切地获取所有内容。有一些重载允许指定 JoinType
,但我需要指定获取类型。现在,我更糊涂了。
是的,您可以使用 FetchType.LAZY、BatchSize、不同的获取模式和 projections 来满足所有三种情况(注意我刚刚用 [=18= 组成了一个 'where' 子句] 以确保我检索到很多行):
执行 JOIN,并从两个 table 中获取。
因为一个项目的顺序是FetchType.LAZY,the default fetch mode will be 'SELECT'所以它只需要设置为'JOIN'来从连接而不是单独查询中获取相关的实体数据:
Session session = entityManager.unwrap(org.hibernate.Session.class); Criteria cr = session.createCriteria(Item.class); cr.add(Restrictions.like("name", "%s%")); cr.setFetchMode("order", FetchMode.JOIN); List results = cr.list(); results.forEach(r -> System.out.println(((Item)r).getOrder().getName()));
生成的单个 SQL 查询:
select this_.id as id1_0_1_, this_.name as name2_0_1_, this_.order_id as order_id3_0_1_, order2_.id as id1_1_0_, order2_.name as name2_1_0_ from item_table this_ left outer join order_table order2_ on this_.order_id=order2_.id where this_.name like ?
做一个 JOIN,从第一个 table 和另一个分开获取。
保留默认的获取模式 'SELECT',为订单创建一个别名以在排序中使用它的列,并使用 projection 到 select 所需的列子集包括外键:
Session session = entityManager.unwrap(org.hibernate.Session.class); Criteria cr = session.createCriteria(Item.class); cr.add(Restrictions.like("name", "%s%")); cr.createAlias("order", "o"); cr.addOrder(org.hibernate.criterion.Order.asc("o.id")); cr.setProjection(Projections.projectionList() .add(Projections.property("id"), "id") .add(Projections.property("name"), "name") .add(Projections.property("order"), "order")) .setResultTransformer(org.hibernate.transform.Transformers.aliasToBean(Item.class)); List results = cr.list(); results.forEach(r -> System.out.println(((Item)r).getOrder().getName()));
结果第一个 SQL 查询:
select this_.id as y0_, this_.name as y1_, this_.order_id as y2_ from item_table this_ inner join order_table o1_ on this_.order_id=o1_.id where this_.name like ? order by o1_.id asc
及后续批次(注意我在订单 class 上使用了
@BatchSize(value=5)
):select order0_.id as id1_1_0_, order0_.name as name2_1_0_ from order_table order0_ where order0_.id in ( ?, ?, ?, ?, ? )
执行 JOIN,但不获取已加入的 table。
与上例相同,但不做任何提示加载延迟加载订单的操作:
Session session = entityManager.unwrap(org.hibernate.Session.class); Criteria cr = session.createCriteria(Item.class); cr.add(Restrictions.like("name", "%s%")); cr.createAlias("order", "o"); cr.addOrder(Order.asc("o.id")); cr.setProjection(Projections.projectionList() .add(Projections.property("id"), "id") .add(Projections.property("name"), "name") .add(Projections.property("order"), "order")) .setResultTransformer(org.hibernate.transform.Transformers.aliasToBean(Item.class)); List results = cr.list(); results.forEach(r -> System.out.println(((Item)r).getName()));
生成的单个 SQL 查询:
select this_.id as y0_, this_.name as y1_, this_.order_id as y2_ from item_table this_ inner join order_table o1_ on this_.order_id=o1_.id where this_.name like ? order by o1_.id asc
我在所有情况下的实体都保持不变:
@Entity
@Table(name = "item_table")
public class Item {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY)
private Order order;
// getters and setters omitted
}
@Entity
@Table(name = "order_table")
@BatchSize(size = 5)
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// getters and setters omitted
}