抓取策略(fetching strategy)是指:当应用程序需要在(Hibernate实体对象图的)关联关系间经行导航的时候,hibernate如何获取关联对象的策略。抓取策略可以在O/R映射的元数据中声明,也可以在特定的HQL 或条件查询(Criteria Query)
中重载声明。
Hibernate3 定义了如下几种抓取策略:
连接抓取(Join fetching) - Hibernate通过 在SELECT
语句使用OUTER JOIN
(外连接)来 获得对象的关联实例或者关联集合,这种情况lazy无效。
查询抓取(Select fetching) - 另外发送一条SELECT
语句抓取当前对象的关联实体或集合。除非你显式的指定lazy="false"
禁止 延迟抓取(lazy fetching),否则只有当你真正访问关联关系的时候,才会执行第二条select语句。
子查询抓取(Subselect fetching) - 另外发送一条SELECT
语句抓取在前面查询到(或者抓取到)的所有实体对象的关联集合。除非你显式的指定lazy="false"
禁止延迟抓取(lazy fetching),否则只有当你真正访问关联关系的时候,才会执行第二条select语句。
批量抓取(Batch fetching) - 对查询抓取的优化方案, 通过指定一个主键或外键列表,Hibernate使用单条SELECT
语句获取一批对象实例或集合。
下面来分别介绍这几种抓取
查询抓取(Select fetching)
项目结构如图
所有的代码和上一节《hibernate加载策略之lazy》中的代码一样,可以参考,
现在多的一端设置fetch
Book.hbm.xml代码:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.myeclipse.pojo">
<class name="Book" table="t_book">
<id name="id">
<generator class="identity" />
</id>
<many-to-one name="category" class="Category" column="category_id"
cascade="save-update" fetch="select" lazy="false"/>
<property name="author" />
<property name="name" column="book_name" />
<property name="price" />
<property name="pubDate" />
<!-- 使用过滤器 -->
<filter name="bookFilter" condition="id=:id"></filter>
</class>
<!-- 过滤器定义 : 定义参数 -->
<filter-def name="bookFilter">
<filter-param name="id" type="integer" />
</filter-def>
</hibernate-mapping>
Category.hbm.xml代码:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.myeclipse.pojo">
<class name="Category" >
<id name="id" >
<generator class="identity" />
</id>
<property name="name" />
<set name="books" inverse="true" >
<key>
<column name="category_id" />
</key>
<one-to-many class="Book" />
</set>
</class>
</hibernate-mapping>
HibernateTest中TestCreateDB代码:
@Test
public void testCreateDB() {
Configuration cfg = new Configuration().configure();
SchemaExport se = new SchemaExport(cfg);
// 第一个参数:是否生成ddl脚本
// 第二个参数:是否执行到数据库中
se.create(true, true);
}
使用Junit4执行,重新生成数据库表。
控制台打印的sql语句如下:
alter table t_book
drop
foreign key FK_cm584cq6cv5yht4jrqal0ocaq
drop table if exists Category
drop table if exists t_book
create table Category (
id integer not null auto_increment,
name varchar(255),
primary key (id)
)
create table t_book (
id integer not null auto_increment,
category_id integer,
author varchar(255),
book_name varchar(255),
price double precision,
pubDate datetime,
primary key (id)
)
alter table t_book
add constraint FK_cm584cq6cv5yht4jrqal0ocaq
foreign key (category_id)
references Category (id)
HibernateTest类中的TestSave()方法代码:
/**
* 保存数据
*/
@Test
public void testSave() {
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
Category category = new Category();
category.setName("文学");
Category category1 = new Category();
category1.setName("历史");
Category category2 = new Category();
category2.setName("仙侠");
Category category3 = new Category();
category3.setName("科幻");
Category category4 = new Category();
category4.setName("恐怖");
Book book = new Book();
book.setName("读者");
book.setPrice(5.6);
book.setAuthor("众人");
book.setPubDate(new Date());
book.setCategory(category);
Book book1 = new Book();
book1.setName("傲慢与偏见");
book1.setPrice(80.0);
book1.setAuthor("简.奥斯汀");
book1.setPubDate(new Date());
book1.setCategory(category1);
Book book2 = new Book();
book2.setName("中国历史");
book2.setPrice(30.0);
book2.setAuthor("人民出版社");
book2.setPubDate(new Date());
book2.setCategory(category1);
Book book3 = new Book();
book3.setName("翩眇之旅");
book3.setPrice(70.0);
book3.setAuthor("萧鼎");
book3.setPubDate(new Date());
book3.setCategory(category2);
Book book4 = new Book();
book4.setName("蓝血人");
book4.setPrice(60.0);
book4.setAuthor("卫斯理");
book4.setPubDate(new Date());
book4.setCategory(category3);
Book book5 = new Book();
book5.setName("我的大学");
book5.setPrice(60.5);
book5.setAuthor("高尔基");
book5.setPubDate(new Date());
book5.setCategory(category);
session.save(book);
session.save(book1);
session.save(book2);
session.save(book3);
session.save(book4);
session.save(book5);
session.save(category4);
tx.commit();
HibernateUtil.closeSession();
}
执行保存数据,sql语句如下:
Hibernate:
insert
into
Category
(name)
values
(?)
Hibernate:
insert
into
t_book
(category_id, author, book_name, price, pubDate)
values
(?, ?, ?, ?, ?)
Hibernate:
insert
into
Category
(name)
values
(?)
Hibernate:
insert
into
t_book
(category_id, author, book_name, price, pubDate)
values
(?, ?, ?, ?, ?)
Hibernate:
insert
into
t_book
(category_id, author, book_name, price, pubDate)
values
(?, ?, ?, ?, ?)
Hibernate:
insert
into
Category
(name)
values
(?)
Hibernate:
insert
into
t_book
(category_id, author, book_name, price, pubDate)
values
(?, ?, ?, ?, ?)
Hibernate:
insert
into
Category
(name)
values
(?)
Hibernate:
insert
into
t_book
(category_id, author, book_name, price, pubDate)
values
(?, ?, ?, ?, ?)
Hibernate:
insert
into
t_book
(category_id, author, book_name, price, pubDate)
values
(?, ?, ?, ?, ?)
Hibernate:
insert
into
Category
(name)
values
(?)
数据库表中的数据如图:
HibernateTest类中的查询代码如下:
/**
* 查询图书
*/
@Test
public void testLoadBook() {
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
Book book = (Book) session.load(Book.class, 1) ;
System.out.println("-------------------------");
System.out.println("bookName = "+book.getName());
System.out.println("===========================");
System.out.println("categoryName = "+book.getCategory().getName());
tx.commit();
HibernateUtil.closeSession();
}
打印的sql语句如下:
INFO: HHH000232: Schema update complete
-------------------------
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
t_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=?
bookName = 读者
===========================
categoryName = 文学
从sql语句可以看出,
当执行Book book = (Book) session.load(Book.class, 1) ;这句话时,并没有打印sql语句,
当查询book.getName(),图书的Name时,打印了查询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
t_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=?
当我们把Book.hbm.xml中的lazy改成proxy时,如图:
执行TestLoadBook()方法,打印的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
t_book book0_
where
book0_.id=?
bookName = 读者
===========================
Hibernate:
select
category0_.id as id1_0_0_,
category0_.name as name2_0_0_
from
Category category0_
where
category0_.id=?
categoryName = 文学
此时当需要获取对应的数据时,才会执行相应的sql语句。
下面在一的一端设置fetch
Category.hbm.xml代码如下:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.myeclipse.pojo">
<class name="Category" >
<id name="id" >
<generator class="identity" />
</id>
<property name="name" />
<set name="books" inverse="true" fetch="select">
<key>
<column name="category_id" />
</key>
<one-to-many class="Book" />
</set>
</class>
</hibernate-mapping>
fetch设置为select,
testGetCategory()方法的代码如下:
@Test
public void testGetCategory() {
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
Category category = (Category) session.get(Category.class, 1);
System.out.println("---------------------------------------------");
System.out.println("category_name=" + category.getName());
System.out.println("============================================");
for (Iterator<Book> iter = category.getBooks().iterator(); iter
.hasNext();) {
System.out.println(iter.next().getName());
}
tx.commit();
HibernateUtil.closeSession();
}
打印的sql语句如下:
Hibernate:
select
category0_.id as id1_0_0_,
category0_.name as name2_0_0_
from
Category category0_
where
category0_.id=?
---------------------------------------------
category_name=文学
============================================
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
t_book books0_
where
books0_.category_id=?
我的大学
读者
从上面可以看出来,get方法,当时就查询了Category表的sql语句,当需要查询Book的数据时,才会执行t_book的数据库表信息。
连接抓取(Join fetching)
先说多段的Book
Book.hbm.xml代码:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.myeclipse.pojo">
<class name="Book" table="t_book">
<id name="id">
<generator class="identity" />
</id>
<many-to-one name="category" class="Category" column="category_id"
cascade="save-update" fetch="join" lazy="proxy"/>
<property name="author" />
<property name="name" column="book_name" />
<property name="price" />
<property name="pubDate" />
<!-- 使用过滤器 -->
<filter name="bookFilter" condition="id=:id"></filter>
</class>
<!-- 过滤器定义 : 定义参数 -->
<filter-def name="bookFilter">
<filter-param name="id" type="integer" />
</filter-def>
</hibernate-mapping>
fetch是join,lazy是proxy
HIbernateTest类中的testLoadBook()方法,代码:
/**
* 查询图书
*/
@Test
public void testLoadBook() {
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
Book book = (Book) session.load(Book.class, 1);
System.out.println("-------------------------");
System.out.println("bookName = " + book.getName());
System.out.println("===========================");
System.out.println("categoryName = " + book.getCategory().getName());
tx.commit();
HibernateUtil.closeSession();
}
控制台打印的sql语句是:
INFO: HHH000232: Schema update complete
-------------------------
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
t_book book0_
left outer join
Category category1_
on book0_.category_id=category1_.id
where
book0_.id=?
bookName = 读者
===========================
categoryName = 文学
由上面可以看到,在需要book的信息时,sql语句使用 left outer join 把Category表中的信息也查询了出来。
下面从一的一端Category来查询
HibernateTest类中的testGetCategory()方法代码:
@Test
public void testGetCategory() {
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
Category category = (Category) session.get(Category.class, 1);
System.out.println("---------------------------------------------");
System.out.println("category_name=" + category.getName());
System.out.println("============================================");
for (Iterator<Book> iter = category.getBooks().iterator(); iter
.hasNext();) {
System.out.println(iter.next().getName());
}
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
t_book books1_
on category0_.id=books1_.category_id
where
category0_.id=?
---------------------------------------------
category_name=文学
============================================
读者
我的大学
从一的一端也是从使用left outer join 方法把t_book表的数据一起查询了出来
接下来我们把Category.hbm.xml和Book.hbm.xml中的lazy和fetch都去掉,执行testLoad()方法,代码如下:
@Test
public void testLoad() {
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
List<Category> list = session.createCriteria(Category.class).list() ;
System.out.println("---------------------------------");
System.out.println("类型个数:"+list.size());
for (Category category : list) {
System.out.println("================================");
System.out.println(category.getName()+"----数据本书:"+category.getBooks().size());
}
tx.commit();
HibernateUtil.closeSession();
}
执行后,打印的sql语句如下:
Hibernate:
select
this_.id as id1_0_0_,
this_.name as name2_0_0_
from
Category this_
---------------------------------
类型个数:5
================================
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
t_book books0_
where
books0_.category_id=?
文学----数据本书:2
================================
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
t_book books0_
where
books0_.category_id=?
历史----数据本书:2
================================
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
t_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
t_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
t_book books0_
where
books0_.category_id=?
恐怖----数据本书:0
子查询(Subselect fetching)
接下来我们在Category.hbm.xml代码中,加入fetch=“subselect” ,如图:
继续testLoad()方法,打印的sql语句如下:
Hibernate:
select
this_.id as id1_0_0_,
this_.name as name2_0_0_
from
Category this_
---------------------------------
类型个数:5
================================
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
t_book books0_
where
books0_.category_id in (
select
this_.id
from
Category this_
)
文学----数据本书:2
================================
历史----数据本书:2
================================
仙侠----数据本书:1
================================
科幻----数据本书:1
================================
恐怖----数据本书:0
由sql语句可以看出,当我们需要查询t_book数据时,sql语句的where语句是:
where
books0_.category_id in (
select
this_.id
from
Category this_
)
一次性将所有的分类Category数据全部查询出来。
假设现在我想查询Category的id是1,3,5的数据,那么方法testLoadWhere代码是:
@Test
public void testLoadWhere() {
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("---------------------------------");
System.out.println("类型个数:"+list.size());
for (Category category : list) {
System.out.println("================================");
System.out.println(category.getName()+"----数据本书:"+category.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
t_book books0_
where
books0_.category_id in (
select
this_.id
from
Category this_
where
this_.id in (
?, ?, ?
)
)
文学----数据本书:2
================================
仙侠----数据本书:1
================================
恐怖----数据本书:0
批量抓取(Batch fetching)
Book.hbm.xml代码修改为:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.myeclipse.pojo">
<class name="Book" table="t_book" batch-size="3">
<id name="id">
<generator class="identity" />
</id>
<many-to-one name="category" class="Category" column="category_id"
cascade="save-update" />
<property name="author" />
<property name="name" column="book_name" />
<property name="price" />
<property name="pubDate" />
<!-- 使用过滤器 -->
<filter name="bookFilter" condition="id=:id"></filter>
</class>
<!-- 过滤器定义 : 定义参数 -->
<filter-def name="bookFilter">
<filter-param name="id" type="integer" />
</filter-def>
</hibernate-mapping>
上面的代码中增加了一个batch-size属性,如图: