hibernate抓取策略

本文介绍了Hibernate的四种抓取策略:连接抓取、查询抓取、子查询抓取和批量抓取,详细解析了每种策略的工作原理,并通过Java项目案例展示了如何在实践中应用这些策略。连接抓取通过外连接获取关联对象,查询抓取和子查询抓取在访问关联关系时执行额外的select语句,批量抓取则是一种优化方案,通过单条select语句获取一批对象。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

抓取策略(fetching strategy)是指:当应用程序需要在(Hibernate实体对象图的)关联关系间进行导航的时候,Hibernate如何获取关联对象的策略。抓取策略可以在O/R映射的元数据中声明,也可以在特定的HQL或条件查询(Criteria Query)中重载声明。

Hibernate定义了如下几种抓取策略:

1、连接抓取(Join fetching)--Hibernate通过在select语句中使用outer join(外链接)来获得对象的关联实例或者关联集合。这时lazy无效。

2、查询抓取(select fetching)--另外发送一条select语句抓取当前对象的关联实体或集合。除非你显示的指定lazy=false禁止延迟抓取,否则只有当你真正访问关联关系的时候,才会执行第二条select语句。

3、子查询抓取(Subselect fetching)--另外发送一条select语句抓取在前面查询到(或者抓取到)的所有实体对象的关联集合。除非你显示的指定lazy=false禁止延迟抓取,否则只有当你真正访问关联关系的时候,才会执行第二条select语句。

4、批量抓取(Batch fetching)--对查询抓取的优化方案,通过指定一个主键或外键列表,Hibernate使用单条select语句获取一批对象实例或集合。

下面我们通过项目案例来简单了解下这几种策略

java项目结构如下:


该项目所有代码跟上一节《hibernate加载策略之lazy》一样,可以参考。

查询抓取(select fetching)

在多的一端设置fetch

Book.hbm.xml配置文件

<hibernate-mapping package="com.test.pojo">  
    <class name="Book" table="book">  
        <id name="id">  
            <generator class="identity" />  
        </id>  
        <many-to-one name="category" class="Category" column="category_id" 
           cascade="save-update" lazy="proxy" fetch="select"/>  
        <property name="author" />  
        <property name="name" column="book_name" />  
        <property name="price" />  
        <property name="pubDate" />  
    </class>  
</hibernate-mapping>

在HibernateTest类中添加代码

@Test
	public void testLoad1(){
		Session session=HibernateUtil.getSession();
		Transaction tx=session.beginTransaction();
		Book book=(Book) session.get(Book.class, 1);
		System.out.println(book.getName());
		System.out.println(book.getCategory().getName());
		tx.commit();
		HibernateUtil.closeSession();
	}

断点执行,当执行到

System.out.println(book.getName());
打印sql语句如下

Hibernate: 
    select
        book0_.id as id1_1_0_,
        book0_.category_id as category2_1_0_,
        book0_.author as author3_1_0_,
        book0_.book_name as book_nam4_1_0_,
        book0_.price as price5_1_0_,
        book0_.pubDate as pubDate6_1_0_ 
    from
        book book0_ 
    where
        book0_.id=?

当执行到

System.out.println(book.getCategory().getName());
打印sql语句如下

Hibernate: 
    select
        category0_.id as id1_0_0_,
        category0_.name as name2_0_0_ 
    from
        Category category0_ 
    where
        category0_.id=?
当我们把Book.hbm.xml中的lazy改为false,如下

<many-to-one name="category" class="Category" column="category_id" 
           cascade="save-update" lazy="false" fetch="select"/>
断点执行testLoad1,当执行到

Book book=(Book) session.get(Book.class, 1);
打印sql语句如下

Hibernate: 
    select
        book0_.id as id1_1_0_,
        book0_.category_id as category2_1_0_,
        book0_.author as author3_1_0_,
        book0_.book_name as book_nam4_1_0_,
        book0_.price as price5_1_0_,
        book0_.pubDate as pubDate6_1_0_ 
    from
        book book0_ 
    where
        book0_.id=?
Hibernate: 
    select
        category0_.id as id1_0_0_,
        category0_.name as name2_0_0_ 
    from
        Category category0_ 
    where
        category0_.id=?

下面我们在一的一端设置fetch

Category.hbm.xml代码

<hibernate-mapping package="com.test.pojo">  
    <class name="Category" >  
        <id name="id" >  
            <generator class="identity" />  
        </id>  
        <property name="name" />  
        <set name="books" inverse="true" lazy="extra" fetch="select">  
            <key>  
                <column name="category_id" />  
            </key>  
            <one-to-many class="Book" />  
        </set>  
    </class>  
  
</hibernate-mapping>

断点执行HibernateTest类中的testGet方法

@Test
	public void testGet(){
		Session session=HibernateUtil.getSession();
		Transaction tx=session.beginTransaction();
		Category category=(Category) session.get(Category.class, 1);
		System.out.println("分类名:"+category.getName());
		for(Iterator<Book> iter=category.getBooks().iterator();iter.hasNext();){
			System.out.println(iter.next().getName());
		}
		tx.commit();
		HibernateUtil.closeSession();
	}

断点执行到

Category category=(Category) session.get(Category.class, 1);
打印sql语句如下

Hibernate: 
    select
        category0_.id as id1_0_0_,
        category0_.name as name2_0_0_ 
    from
        Category category0_ 
    where
        category0_.id=?
执行到

System.out.println(iter.next().getName());
打印sql语句如下

Hibernate: 
    select
        books0_.category_id as category2_0_0_,
        books0_.id as id1_1_0_,
        books0_.id as id1_1_1_,
        books0_.category_id as category2_1_1_,
        books0_.author as author3_1_1_,
        books0_.book_name as book_nam4_1_1_,
        books0_.price as price5_1_1_,
        books0_.pubDate as pubDate6_1_1_ 
    from
        book books0_ 
    where
        books0_.category_id=?
从上面可以看出来,get方法,当时就查询了Category表的sql语句,当需要查询Book的数据时,才会执行book的 数据库 表信息。

连接抓取(Join fetching)

先看在多的一端设置fetch

将Book.hbm.xml中的fetch设为join,lazy设为proxy,如下

<many-to-one name="category" class="Category" column="category_id" 
           cascade="save-update" lazy="proxy" fetch="join"/>
执行testLoad1,打印信息如下:

Hibernate: 
    select
        book0_.id as id1_1_0_,
        book0_.category_id as category2_1_0_,
        book0_.author as author3_1_0_,
        book0_.book_name as book_nam4_1_0_,
        book0_.price as price5_1_0_,
        book0_.pubDate as pubDate6_1_0_,
        category1_.id as id1_0_1_,
        category1_.name as name2_0_1_ 
    from
        book book0_ 
    left outer join
        Category category1_ 
            on book0_.category_id=category1_.id 
    where
        book0_.id=?
读者
文学
由上面可以看到,在需要book的信息时,sql语句使用 left outer join 把Category表中的信息也查询了出来。


再来从一的一端来查询

将Category.hbm.xml中的fetch设为join,lazy默认为true

<set name="books" inverse="true" fetch="join"> 
执行testLoad代码

@Test
	public void testLoad(){
		Session session=HibernateUtil.getSession();
		Transaction tx=session.beginTransaction();
		Category category=(Category) session.get(Category.class, 1);
		System.out.println("分类名:"+category.getName());
		System.out.println("对应的书大小:"+category.getBooks().size());
		tx.commit();
		HibernateUtil.closeSession();
	}
打印信息如下

Hibernate: 
    select
        category0_.id as id1_0_0_,
        category0_.name as name2_0_0_,
        books1_.category_id as category2_0_1_,
        books1_.id as id1_1_1_,
        books1_.id as id1_1_2_,
        books1_.category_id as category2_1_2_,
        books1_.author as author3_1_2_,
        books1_.book_name as book_nam4_1_2_,
        books1_.price as price5_1_2_,
        books1_.pubDate as pubDate6_1_2_ 
    from
        Category category0_ 
    left outer join
        book books1_ 
            on category0_.id=books1_.category_id 
    where
        category0_.id=?
分类名:文学
对应的书大小:1

从一的一端也是使用left outer join方法把book表中的数据一起查询出来了。

接下来我们把Book.hbm.xml和Category.hbm.xml中的lazy和fetch都去掉,执行testLoad2,代码如下

@Test
	public void testLoad2(){
		Session session=HibernateUtil.getSession();
		Transaction tx=session.beginTransaction();
		List<Category> list=session.createCriteria(Category.class).list();
		System.out.println("类型个数:"+list.size());
		for(Category cate:list){
			System.out.println(cate.getName()+"---书籍本数:"+cate.getBooks().size());
		}
		tx.commit();
		HibernateUtil.closeSession();
	}
打印sql语句如下

Hibernate: 
    select
        this_.id as id1_0_0_,
        this_.name as name2_0_0_ 
    from
        Category this_
类型个数:6
Hibernate: 
    select
        books0_.category_id as category2_0_0_,
        books0_.id as id1_1_0_,
        books0_.id as id1_1_1_,
        books0_.category_id as category2_1_1_,
        books0_.author as author3_1_1_,
        books0_.book_name as book_nam4_1_1_,
        books0_.price as price5_1_1_,
        books0_.pubDate as pubDate6_1_1_ 
    from
        book books0_ 
    where
        books0_.category_id=?
文学---书籍本数:1
Hibernate: 
    select
        books0_.category_id as category2_0_0_,
        books0_.id as id1_1_0_,
        books0_.id as id1_1_1_,
        books0_.category_id as category2_1_1_,
        books0_.author as author3_1_1_,
        books0_.book_name as book_nam4_1_1_,
        books0_.price as price5_1_1_,
        books0_.pubDate as pubDate6_1_1_ 
    from
        book books0_ 
    where
        books0_.category_id=?
科幻---书籍本数:1
Hibernate: 
    select
        books0_.category_id as category2_0_0_,
        books0_.id as id1_1_0_,
        books0_.id as id1_1_1_,
        books0_.category_id as category2_1_1_,
        books0_.author as author3_1_1_,
        books0_.book_name as book_nam4_1_1_,
        books0_.price as price5_1_1_,
        books0_.pubDate as pubDate6_1_1_ 
    from
        book books0_ 
    where
        books0_.category_id=?
言情---书籍本数:1
Hibernate: 
    select
        books0_.category_id as category2_0_0_,
        books0_.id as id1_1_0_,
        books0_.id as id1_1_1_,
        books0_.category_id as category2_1_1_,
        books0_.author as author3_1_1_,
        books0_.book_name as book_nam4_1_1_,
        books0_.price as price5_1_1_,
        books0_.pubDate as pubDate6_1_1_ 
    from
        book books0_ 
    where
        books0_.category_id=?
历史---书籍本数:1
Hibernate: 
    select
        books0_.category_id as category2_0_0_,
        books0_.id as id1_1_0_,
        books0_.id as id1_1_1_,
        books0_.category_id as category2_1_1_,
        books0_.author as author3_1_1_,
        books0_.book_name as book_nam4_1_1_,
        books0_.price as price5_1_1_,
        books0_.pubDate as pubDate6_1_1_ 
    from
        book books0_ 
    where
        books0_.category_id=?
漫画---书籍本数:1
Hibernate: 
    select
        books0_.category_id as category2_0_0_,
        books0_.id as id1_1_0_,
        books0_.id as id1_1_1_,
        books0_.category_id as category2_1_1_,
        books0_.author as author3_1_1_,
        books0_.book_name as book_nam4_1_1_,
        books0_.price as price5_1_1_,
        books0_.pubDate as pubDate6_1_1_ 
    from
        book books0_ 
    where
        books0_.category_id=?
悬疑---书籍本数:1
从上面的sql语句可以看出sql语句重复

子查询抓取(Subselect fetching)

我们在Category.hbm.xml中加上fetch=subselect

<set name="books" inverse="true" fetch="subselect">  
继续执行testLoad2,打印sql语句如下

Hibernate: 
    select
        this_.id as id1_0_0_,
        this_.name as name2_0_0_ 
    from
        Category this_
类型个数:6
Hibernate: 
    select
        books0_.category_id as category2_0_1_,
        books0_.id as id1_1_1_,
        books0_.id as id1_1_0_,
        books0_.category_id as category2_1_0_,
        books0_.author as author3_1_0_,
        books0_.book_name as book_nam4_1_0_,
        books0_.price as price5_1_0_,
        books0_.pubDate as pubDate6_1_0_ 
    from
        book books0_ 
    where
        books0_.category_id in (
            select
                this_.id 
            from
                Category this_
        )
文学---书籍本数:1
科幻---书籍本数:1
言情---书籍本数:1
历史---书籍本数:1
漫画---书籍本数:1
悬疑---书籍本数:1

一次性将所有的分类Category的数据全部查询出来。


假设现在我想查询Category的id是1,3,5的数据,那么方法testLoad2的代码如下

@Test
	public void testLoad2(){
		Session session=HibernateUtil.getSession();
		Transaction tx=session.beginTransaction();
		List<Category> list=session.createCriteria(Category.class)
				.add(Restrictions.in("id", new Integer[]{1,3,5}))
				.list();
		System.out.println("类型个数:"+list.size());
		for(Category cate:list){
			System.out.println(cate.getName()+"---书籍本数:"+cate.getBooks().size());
		}
		tx.commit();
		HibernateUtil.closeSession();
	}
打印sql语句如下

Hibernate: 
    select
        this_.id as id1_0_0_,
        this_.name as name2_0_0_ 
    from
        Category this_ 
    where
        this_.id in (
            ?, ?, ?
        )
类型个数:3
Hibernate: 
    select
        books0_.category_id as category2_0_1_,
        books0_.id as id1_1_1_,
        books0_.id as id1_1_0_,
        books0_.category_id as category2_1_0_,
        books0_.author as author3_1_0_,
        books0_.book_name as book_nam4_1_0_,
        books0_.price as price5_1_0_,
        books0_.pubDate as pubDate6_1_0_ 
    from
        book books0_ 
    where
        books0_.category_id in (
            select
                this_.id 
            from
                Category this_ 
            where
                this_.id in (
                    ?, ?, ?
                )
        )
文学---书籍本数:1
言情---书籍本数:1
漫画---书籍本数:1

批量抓取(Batch fetching)

数据量大的时候采用此方法有意义,数据量小时,采用此方法效果不明显。





































评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值