首页 > 解决方案 > 如何使用旧的休眠标准进行批处理?

问题描述

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

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

看起来如果没有明确指定fetch=FetchType.LAZY,一切都会像第一种情况一样被急切地获取,这有时太糟糕了。我想,使用Criteria#setFetchMode,我可以得到第三种情况。我还没有尝试过,因为我仍然错过了第二种情况。我知道这是可能的,因为有@BatchSize注释。

更新

看起来使用createAlias()线索可以急切地获取所有内容。有一些重载允许指定JoinType,但我需要指定 fetch 类型。现在,我更加困惑了。

标签: javaperformancehibernatejoinhibernate-criteria

解决方案


Yes you can satisfy all three cases using FetchType.LAZY, BatchSize, the different fetch modes, and projections (note I just made up a 'where' clause with Restrictions.like("name", "%s%") to ensure that I retrieved many rows):

  1. Do a JOIN, and fetch from both tables.

    Because the order of an item is FetchType.LAZY, the default fetch mode will be 'SELECT' so it just needs to be set as 'JOIN' to fetch the related entity data from a join rather than separate 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()));
    

    The resulting single SQL query:

    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. Do a JOIN, fetch from the first table and the separately from the other.

    Leave the fetch mode as the default 'SELECT', create an alias for the order to use it's columns in sorting, and use a projection to select the desired subset of columns including the foreign key:

    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()));
    
    

    The resulting first SQL query:

    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
    

    and subsequent batches (note I used @BatchSize(value=5) on the Order class):

    select
        order0_.id as id1_1_0_,
        order0_.name as name2_1_0_ 
    from
        order_table order0_ 
    where
        order0_.id in (
            ?, ?, ?, ?, ?
        )
    
  3. Do a JOIN, but do not fetch the joined table.

    Same as the previous case, but don't do anything to prompt the loading of the lazy-loaded orders:

    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()));
    

    The resulting single SQL query:

    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
    

My entities for all cases remained the same:

@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
}

推荐阅读