【问题标题】:How to get batching using the old hibernate criteria?如何使用旧的休眠标准进行批处理?
【发布时间】:2020-05-24 21:57:19
【问题描述】:

我仍在使用旧的org.hibernate.Criteria,并且对获取模式越来越感到困惑。在各种查询中,我需要以下所有变体,因此无法通过注释对其进行控制。我只是将所有内容都切换到@ManyToOne(fetch=FetchType.LAZY),否则,查询中的任何内容都不会更改。

到目前为止,我能找到的要么涉及 HQL 或 JPA2,要么只提供两种选择,但我需要它来满足旧标准和(至少)以下三种情况:

  • 执行 JOIN,并从两个表中提取。除非数据过于冗余(例如,主数据很大或在结果中重复多次),否则这是可以的。在 SQL 中,我会写
    SELECT * FROM item JOIN order on item.order_id = order.id
    WHERE ...;
  • 执行 JOIN,从第一个表中提取,然后与另一个表分离。这通常是上一个查询的更有效的变体。在 SQL 中,我会写
    SELECT item.* FROM item JOIN order on item.order_id = order.id
    WHERE ...;

    SELECT order.* FROM order WHERE ...;
  • 执行 JOIN,但不获取连接的表。这很有用,例如,用于根据其他表的数据进行排序。在 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,但我需要指定获取类型。现在,我更加困惑了。

【问题讨论】:

  • 快速 ping - 下面的答案对你有用吗@maaartinus?很感兴趣。
  • @ThomasPortwood 抱歉,我还没有时间尝试(这是困扰我很久的问题,但目前还有其他问题)。关于第二种和第三种情况需要投影所有属性,这在某种程度上是不令人满意的,不是吗?
  • 我绝对同意这就是我想确认的原因。我很高兴我能够回答有没有办法......这个问题,但我很想听听您是否找到了更优雅的方式。
  • @ThomasPortwood 我想,接受你的回答是最好的,至少现在是这样。当我谈到它时,我可能会重新考虑;)
  • 谢谢先生。我会留意更令人满意的方法。

标签: java performance hibernate join hibernate-criteria


【解决方案1】:

是的,您可以使用 FetchType.LAZY、BatchSize、不同的获取模式和 projections 来满足所有三种情况(注意我刚刚用 Restrictions.like("name", "%s%") 组成了一个“where”子句,以确保我检索到许多行):

  1. 执行 JOIN,并从两个表中获取数据。

    因为一个item的顺序是FetchType.LAZY,the default fetch mode will be 'SELECT'所以只需要设置为'JOIN'就可以从一个join而不是单独的query中获取相关的实体数据:

    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 ?
    
  2. 做一个 JOIN,从第一个表中获取,并与另一个分开。

    将获取模式保留为默认的“SELECT”,为订单创建别名以在排序中使用其列,并使用projection 选择所需的列子集,包括外键:

    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
    

    以及后续批次(注意我在 Order 类中使用了@BatchSize(value=5)):

    select
        order0_.id as id1_1_0_,
        order0_.name as name2_1_0_ 
    from
        order_table order0_ 
    where
        order0_.id in (
            ?, ?, ?, ?, ?
        )
    
  3. 执行 JOIN,但不获取连接的表。

    与上例相同,但不做任何提示加载延迟加载订单的操作:

    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
}

【讨论】:

    猜你喜欢
    • 2016-04-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-29
    • 1970-01-01
    • 2013-03-03
    • 2014-10-07
    • 2011-07-23
    相关资源
    最近更新 更多