Hibernate(9)之抓取策略

Hibernate(9)之抓取策略

微信搜索 zze_coding 或扫描 👉 二维码关注我的微信公众号获取更多资源推送:

概述

通过一个对象抓取关联对象需要发送 SQL ,SQL 如何发送及 SQL 的发送规则就可以通过抓取策略进行配置。

通过 <set><many-to-one> 上的 fetch 属性进行配置。
要考虑 fetch 和这些标签上的 lazy 如何配置才能最大化优化发送的 SQL 语句。

set 标签上的 fetch 和 lazy

fetch

指定抓取策略,控制 SQL 语句格式。
在这里 fetch 有三个可选值:

  • select: 默认值,发送普通 select 语句查询关联对象。
  • join:发送一条迫切左外连接查询关联对象。
  • subselect:发送子查询查询关联对象。

lazy

控制查询关联对象时是否使用懒加载。

在这里 lazy 有三个可选值:

  • true:默认值,查询关联对象时,使用延迟加载。
  • false:查询关联对象时,不使用延迟加载。
  • extra:及其懒惰。

示例

例 1:fetch="select" lazy="true"

<!-- com/zze/bean/Class.hbm.xml -->
<!-- 默认值:-->
<set name="students" cascade="save-update" fetch="select" lazy="true">
Session session = HibernateUtil.getCurrentSession();
Transaction transaction = session.beginTransaction();
Class clazz = session.get(Class.class, 1);
for (Student student : clazz.getStudents()) {
    System.out.println(student);// 发送 SQL
}
transaction.commit();
/*
Hibernate:
    select
        class0_.id as id1_0_0_,
        class0_.name as name2_0_0_
    from
        class class0_
    where
        class0_.id=?
Hibernate:
    select
        students0_.cid as cid5_1_0_,
        students0_.id as id1_1_0_,
        students0_.id as id1_1_1_,
        students0_.name as name2_1_1_,
        students0_.age as age3_1_1_,
        students0_.gender as gender4_1_1_,
        students0_.cid as cid5_1_1_
    from
        student students0_
    where
        students0_.cid=?
Student{id=2, name='张三'}
Student{id=1, name='李四'}
 */

例 2:fetch="select" lazy="false"

<!-- com/zze/bean/Class.hbm.xml -->
<set name="students" cascade="save-update" fetch="select" lazy="false">
Session session = HibernateUtil.getCurrentSession();
Transaction transaction = session.beginTransaction();
Class clazz = session.get(Class.class, 1); //发送 2 条 SQL
for (Student student : clazz.getStudents()) {
    System.out.println(student);
}
transaction.commit();
/*
Hibernate:
    select
        class0_.id as id1_0_0_,
        class0_.name as name2_0_0_
    from
        class class0_
    where
        class0_.id=?
Hibernate:
    select
        students0_.cid as cid5_1_0_,
        students0_.id as id1_1_0_,
        students0_.id as id1_1_1_,
        students0_.name as name2_1_1_,
        students0_.age as age3_1_1_,
        students0_.gender as gender4_1_1_,
        students0_.cid as cid5_1_1_
    from
        student students0_
    where
        students0_.cid=?
Student{id=2, name='张三'}
Student{id=1, name='李四'}
 */

例 3:fetch="select" lazy="extra"

<!-- com/zze/bean/Class.hbm.xml -->
<set name="students" cascade="save-update" fetch="select" lazy="extra">
Session session = HibernateUtil.getCurrentSession();
Transaction transaction = session.beginTransaction();
Class clazz = session.get(Class.class, 1);
System.out.println(clazz.getStudents().size()); // 针对数量发送一条sql
transaction.commit();
/*
Hibernate:
    select
        class0_.id as id1_0_0_,
        class0_.name as name2_0_0_
    from
        class class0_
    where
        class0_.id=?
Hibernate:
    select
        count(id)
    from
        student
    where
        cid =?
 */

例 4:fetch="join" lazy="true"

<!-- com/zze/bean/Class.hbm.xml -->
<!-- fetch="join" 时,lazy 失效 -->
<set name="students" cascade="save-update" fetch="join" lazy="true">
Session session = HibernateUtil.getCurrentSession();
Transaction transaction = session.beginTransaction();
Class clazz = session.get(Class.class, 1);// 发送 SQL
for (Student student : clazz.getStudents()) {
    System.out.println(student);
}
transaction.commit();
/*
Hibernate:
    select
        class0_.id as id1_0_0_,
        class0_.name as name2_0_0_,
        students1_.cid as cid5_1_1_,
        students1_.id as id1_1_1_,
        students1_.id as id1_1_2_,
        students1_.name as name2_1_2_,
        students1_.age as age3_1_2_,
        students1_.gender as gender4_1_2_,
        students1_.cid as cid5_1_2_
    from
        class class0_
    left outer join
        student students1_
            on class0_.id=students1_.cid
    where
        class0_.id=?
Student{id=2, name='张三'}
Student{id=1, name='李四'}
 */

例 5:fetch="subselect" lazy="true"

<!-- com/zze/bean/Class.hbm.xml -->
<set name="students" cascade="save-update" fetch="subselect" lazy="true"> */
Session session = HibernateUtil.getCurrentSession();
Transaction transaction = session.beginTransaction();
Query query = session.createQuery("from Class");
List<Class> list = query.list();
for (Class clazz : list) {
    Set<Student> students = clazz.getStudents(); // 发送 SQL
    System.out.println(students);
}
transaction.commit();
/*
Hibernate:
    select
        class0_.id as id1_0_,
        class0_.name as name2_0_
    from
        class class0_
Hibernate:
    select
        students0_.cid as cid5_1_1_,
        students0_.id as id1_1_1_,
        students0_.id as id1_1_0_,
        students0_.name as name2_1_0_,
        students0_.age as age3_1_0_,
        students0_.gender as gender4_1_0_,
        students0_.cid as cid5_1_0_
    from
        student students0_
    where
        students0_.cid in (
            select
                class0_.id
            from
                class class0_
        )
[Student{id=1, name='李四'}, Student{id=2, name='张三'}]
[Student{id=4, name='赵六'}, Student{id=3, name='王五'}]
[Student{id=5, name='吴刚'}, Student{id=6, name='王如花'}]
[Student{id=7, name='李志静'}, Student{id=8, name='何王天'}]
 */

例 6:fetch="subselect" lazy="false"

<!-- com/zze/bean/Class.hbm.xml -->
<set name="students" cascade="save-update" fetch="subselect" lazy="false">
Session session = HibernateUtil.getCurrentSession();
Transaction transaction = session.beginTransaction();
Query query = session.createQuery("from Class");
List<Class> list = query.list(); // 发送 SQL
for (Class clazz : list) {
    Set<Student> students = clazz.getStudents();
    System.out.println(students);
}
transaction.commit();
/*
Hibernate:
    select
        class0_.id as id1_0_,
        class0_.name as name2_0_
    from
        class class0_
Hibernate:
    select
        students0_.cid as cid5_1_1_,
        students0_.id as id1_1_1_,
        students0_.id as id1_1_0_,
        students0_.name as name2_1_0_,
        students0_.age as age3_1_0_,
        students0_.gender as gender4_1_0_,
        students0_.cid as cid5_1_0_
    from
        student students0_
    where
        students0_.cid in (
            select
                class0_.id
            from
                class class0_
        )
[Student{id=1, name='李四'}, Student{id=2, name='张三'}]
[Student{id=4, name='赵六'}, Student{id=3, name='王五'}]
[Student{id=5, name='吴刚'}, Student{id=6, name='王如花'}]
[Student{id=7, name='李志静'}, Student{id=8, name='何王天'}]
 */

many-to-one 标签上的 fetch 和 lazy

fetch

指定抓取策略,控制 SQL 语句格式。

在这里 fetch 有两个可选值:

  • select:默认值,发送普通的 select 语句查询关联对象。
  • join:发送一条迫切左外连接查询关联对象。

lazy

控制查询关联对象时是否使用懒加载。

在这里 lazy 有三个可选值:

  • proxy:默认值,引用关联对象类的类级别加载策略。
  • flase:查询关联对象时,不使用延迟加载。
  • no-proxy:不使用。

示例

例 1:fetch="select" lazy="proxy"

<!-- com/zze/bean/Student.hbm.xml -->
<!-- 默认值 -->
<many-to-one name="clazz" column="cid" class="com.zze.bean.Class" fetch="select" lazy="proxy"/>
<!-- com/zze/bean/Class.hbm.xml -->
<class name="com.zze.bean.Class" table="class" lazy="true">
Session session = HibernateUtil.getCurrentSession();
Transaction transaction = session.beginTransaction();
Student student = session.get(Student.class, 1);
System.out.println(student.getClazz()); // 发送 SQL
transaction.commit();
/*
Hibernate:
    select
        student0_.id as id1_1_0_,
        student0_.name as name2_1_0_,
        student0_.age as age3_1_0_,
        student0_.gender as gender4_1_0_,
        student0_.cid as cid5_1_0_
    from
        student student0_
    where
        student0_.id=?
Hibernate:
    select
        class0_.id as id1_0_0_,
        class0_.name as name2_0_0_
    from
        class class0_
    where
        class0_.id=?
Class{id=1, name='软件一班'}
 */

例 2:fetch="select" lazy="proxy"

<!-- com/zze/bean/Student.hbm.xml -->
<!-- 默认值 -->
<many-to-one name="clazz" column="cid" class="com.zze.bean.Class" fetch="select" lazy="proxy"/>
<!-- com/zze/bean/Class.hbm.xml -->
<class name="com.zze.bean.Class" table="class" lazy="false">
Session session = HibernateUtil.getCurrentSession();
Transaction transaction = session.beginTransaction();
Student student = session.get(Student.class, 1);// 一次发送 2 条 SQL
System.out.println(student.getClazz());
transaction.commit();
/*
Hibernate:
    select
        student0_.id as id1_1_0_,
        student0_.name as name2_1_0_,
        student0_.age as age3_1_0_,
        student0_.gender as gender4_1_0_,
        student0_.cid as cid5_1_0_
    from
        student student0_
    where
        student0_.id=?
Hibernate:
    select
        class0_.id as id1_0_0_,
        class0_.name as name2_0_0_
    from
        class class0_
    where
        class0_.id=?
Class{id=1, name='软件一班'}
 */

例 3:fetch="join" lazy="proxy"

<!-- com/zze/bean/Student.hbm.xml -->
<!-- fetch="join" 时,lazy 失效 -->
<many-to-one name="clazz" column="cid" class="com.zze.bean.Class" fetch="join" lazy="proxy"/>
Session session = HibernateUtil.getCurrentSession();
Transaction transaction = session.beginTransaction();
Student student = session.get(Student.class, 1);// 一条关联 SQL
System.out.println(student.getClazz());
transaction.commit();
/*
Hibernate:
    select
        student0_.id as id1_1_0_,
        student0_.name as name2_1_0_,
        student0_.age as age3_1_0_,
        student0_.gender as gender4_1_0_,
        student0_.cid as cid5_1_0_,
        class1_.id as id1_0_1_,
        class1_.name as name2_0_1_
    from
        student student0_
    left outer join
        class class1_
            on student0_.cid=class1_.id
    where
        student0_.id=?
Class{id=1, name='软件一班'}
 */

批量抓取

指的是一批指定数量的关联数据一起查询。

例 1:查询所有班级及对应班级下的学生信息。

默认情况:

// 默认情况下,查询所有班级会发一条 SQL,然后每次查询班级上的学生也会发送一条 SQL
// 即如下有 4 个班级,会发 5 条SQL
Session session = HibernateUtil.getCurrentSession();
    Transaction transaction = session.beginTransaction();
    Query query = session.createQuery("from Class");
    List<Class> list = query.list();
    for (Class clazz : list) {
        System.out.println(clazz);
        for (Student student : clazz.getStudents()) {
            System.out.println(student);
        }
    }
    transaction.commit();
    /*
    Hibernate:
        select
            class0_.id as id1_0_,
            class0_.name as name2_0_
        from
            class class0_
    Class{id=1, name='软件一班'}
    Hibernate:
        select
            students0_.cid as cid5_1_0_,
            students0_.id as id1_1_0_,
            students0_.id as id1_1_1_,
            students0_.name as name2_1_1_,
            students0_.age as age3_1_1_,
            students0_.gender as gender4_1_1_,
            students0_.cid as cid5_1_1_
        from
            student students0_
        where
            students0_.cid=?
    Student{id=1, name='李四'}
    Student{id=2, name='张三'}
    Class{id=2, name='软件二班'}
    Hibernate:
        select
            students0_.cid as cid5_1_0_,
            students0_.id as id1_1_0_,
            students0_.id as id1_1_1_,
            students0_.name as name2_1_1_,
            students0_.age as age3_1_1_,
            students0_.gender as gender4_1_1_,
            students0_.cid as cid5_1_1_
        from
            student students0_
        where
            students0_.cid=?
    Student{id=4, name='王五'}
    Student{id=3, name='赵六'}
    Class{id=3, name='汽修一班'}
    Hibernate:
        select
            students0_.cid as cid5_1_0_,
            students0_.id as id1_1_0_,
            students0_.id as id1_1_1_,
            students0_.name as name2_1_1_,
            students0_.age as age3_1_1_,
            students0_.gender as gender4_1_1_,
            students0_.cid as cid5_1_1_
        from
            student students0_
        where
            students0_.cid=?
    Student{id=5, name='王如花'}
    Student{id=6, name='吴刚'}
    Class{id=4, name='汽修二班'}
    Hibernate:
        select
            students0_.cid as cid5_1_0_,
            students0_.id as id1_1_0_,
            students0_.id as id1_1_1_,
            students0_.name as name2_1_1_,
            students0_.age as age3_1_1_,
            students0_.gender as gender4_1_1_,
            students0_.cid as cid5_1_1_
        from
            student students0_
        where
            students0_.cid=?
    Student{id=8, name='李志静'}
    Student{id=7, name='何王天'}
     */

修改配置文件:

<!-- com/zze/bean/Class.hbm.xml -->
<set name="students" cascade="save-update" batch-size="4">
// 一次抓取 4 条,就只需要发送 2 次 SQL
// 一次查询所有班级,一次查询所有班级下的学生
Session session = HibernateUtil.getCurrentSession();
Transaction transaction = session.beginTransaction();
Query query = session.createQuery("from Class");
List<Class> list = query.list();
for (Class clazz : list) {
    System.out.println(clazz);
    for (Student student : clazz.getStudents()) {
        System.out.println(student);
    }
}
transaction.commit();
/*
Hibernate:
    select
        class0_.id as id1_0_,
        class0_.name as name2_0_
    from
        class class0_
Class{id=1, name='软件一班'}
Hibernate:
    select
        students0_.cid as cid5_1_1_,
        students0_.id as id1_1_1_,
        students0_.id as id1_1_0_,
        students0_.name as name2_1_0_,
        students0_.age as age3_1_0_,
        students0_.gender as gender4_1_0_,
        students0_.cid as cid5_1_0_
    from
        student students0_
    where
        students0_.cid in (
            ?, ?, ?, ?
        )
Student{id=1, name='李四'}
Student{id=2, name='张三'}
Class{id=2, name='软件二班'}
Student{id=4, name='王五'}
Student{id=3, name='赵六'}
Class{id=3, name='汽修一班'}
Student{id=5, name='王如花'}
Student{id=6, name='吴刚'}
Class{id=4, name='汽修二班'}
Student{id=7, name='何王天'}
Student{id=8, name='李志静'}
 */

例 2:查询所有学生及所有学生所属班级信息。

// 默认情况下,查询所有学生会发送 1 条 SQL,然后每次查询每个学生的所属班级时也会发送一条 SQL,
// 有 8 个学生,但一共有 4 个班级,由于一级缓存的作用,查询班级需要发送 4 条 SQL
// 共发 5 条 SQL
Session session = HibernateUtil.getCurrentSession();
Transaction transaction = session.beginTransaction();
Query query = session.createQuery("from Student");
List<Student> list = query.list();
for (Student student : list) {
    System.out.println(student.getClazz());
}
transaction.commit();
/*
Hibernate:
    select
        student0_.id as id1_1_,
        student0_.name as name2_1_,
        student0_.age as age3_1_,
        student0_.gender as gender4_1_,
        student0_.cid as cid5_1_
    from
        student student0_
Hibernate:
    select
        class0_.id as id1_0_0_,
        class0_.name as name2_0_0_
    from
        class class0_
    where
        class0_.id=?
Class{id=1, name='软件一班'}
Class{id=1, name='软件一班'}
Hibernate:
    select
        class0_.id as id1_0_0_,
        class0_.name as name2_0_0_
    from
        class class0_
    where
        class0_.id=?
Class{id=2, name='软件二班'}
Class{id=2, name='软件二班'}
Hibernate:
    select
        class0_.id as id1_0_0_,
        class0_.name as name2_0_0_
    from
        class class0_
    where
        class0_.id=?
Class{id=3, name='汽修一班'}
Class{id=3, name='汽修一班'}
Hibernate:
    select
        class0_.id as id1_0_0_,
        class0_.name as name2_0_0_
    from
        class class0_
    where
        class0_.id=?
Class{id=4, name='汽修二班'}
Class{id=4, name='汽修二班'}
 */

修改配置文件:

<!-- com/zze/bean/Class.hbm.xml -->
<class name="com.zze.bean.Class" table="class" batch-size="3">
<!-- many-to-one 关系时需在关联属性类映射文件下配置 batch-size -->
// 查询所有学生 1 条 SQL,查询所有班级每次抓取 3 条,一共 4 个班级,会发 2 次 SQL
Session session = HibernateUtil.getCurrentSession();
Transaction transaction = session.beginTransaction();
Query query = session.createQuery("from Student");
List<Student> list = query.list();
for (Student student : list) {
    System.out.println(student.getClazz());
}
transaction.commit();
/*
Hibernate:
    select
        student0_.id as id1_1_,
        student0_.name as name2_1_,
        student0_.age as age3_1_,
        student0_.gender as gender4_1_,
        student0_.cid as cid5_1_
    from
        student student0_
Hibernate:
    select
        class0_.id as id1_0_0_,
        class0_.name as name2_0_0_
    from
        class class0_
    where
        class0_.id in (
            ?, ?, ?
        )
Class{id=1, name='软件一班'}
Class{id=1, name='软件一班'}
Class{id=2, name='软件二班'}
Class{id=2, name='软件二班'}
Class{id=3, name='汽修一班'}
Class{id=3, name='汽修一班'}
Hibernate:
    select
        class0_.id as id1_0_0_,
        class0_.name as name2_0_0_
    from
        class class0_
    where
        class0_.id=?
Class{id=4, name='汽修二班'}
Class{id=4, name='汽修二班'}
 */

Copyright: 采用 知识共享署名4.0 国际许可协议进行许可

Links: https://www.zze.xyz/archives/hibernate9.html

Buy me a cup of coffee ☕.