[size=medium]
Hibernate有一些fetching策略,来优化Hibernate所生成的select语句,以尽可能地提高效率。在映射关系中声明fetching策略,定义Hibernate怎样获取其相关的集合和实体。
[b]影响关系映射抓取的cfg配置:[/b]
[color=green]hibernate.max_fetch_depth[/color]
为单向关联(一对一, 多对一)的外连接抓取(outer join fetch)树设置最大深度. 值为0意味着将关闭默认的外连接抓取.
取值 建议在0到3之间取值
[color=green]hibernate.default_batch_fetch_size[/color]
为Hibernate关联的批量抓取设置默认数量.
取值 建议的取值为4, 8, 和16
如果你的数据库支持ANSI, Oracle或Sybase风格的外连接, 外连接抓取通常能通过限制往返数据库次数 (更多的工作交由数据库自己来完成)来提高效率. 外连接抓取允许在单个SELECTSQL语句中, 通过many-to-one, one-to-many, many-to-many和one-to-one关联获取连接对象的整个对象图.
将hibernate.max_fetch_depth设为0能在全局 范围内禁止外连接抓取. 设为1或更高值能启用one-to-one和many-to-one外连接关联的外连接抓取, 它们通过 fetch="join"来映射.
[b]Hibernate有四种fetching策略,当同时配置了JPA的fetch时,会优先使用Hibernate的注解:[/b]
[list]
[*]fetch-“join”:禁用延迟加载,总是立即加载所有的集合和实体。
[*]fetch-“select”(默认):延迟加载所有的集合和实体。
[*]fetch-“subselect”:将其集合组织到一个子查询语句中。
[*]batch-size=”N”:获取上限为“N”个的集合或实体,没有记录。
[/list]
[/size]
[size=medium]在XML映射文件中声明fetch策略:[/size]
...
table="stock_daily_record" batch-size="10" fetch="select">
[size=medium]以标注的形式声明fetch策略:[/size]
...
@Entity
@Table(name = "stock")
public class Stock implements Serializable{
...
@OneToMany(fetch = FetchType.LAZY, mappedBy = "stock")
@Cascade(CascadeType.ALL)
@Fetch(FetchMode.SELECT)
@BatchSize(size = 10)
public Set getStockDailyRecords() {
return this.stockDailyRecords;
}
...
}
[size=medium]
[b]下面探讨fetch策略如何影响到Hibernate生成的SQL语句[/b]
[color=blue]1、@Fetch(FetchMode.SELECT)[/color]
另外发送一条SELECT语句抓取当前对象的关联实体或集合。除非你显式的指定lazy="false"禁止延迟抓取(lazy fetching),否则只有当你真正访问关联关系的时候,才会执行第二条select语句。
这是默认的fetch策略。请看示例代码:
[/size]
//call select from stock
Stock stock = (Stock)session.get(Stock.class, 114);
Set sets = stock.getStockDailyRecords();
//call select from stock_daily_record
for ( Iterator iter = sets.iterator();iter.hasNext(); ) {
StockDailyRecord sdr = (StockDailyRecord) iter.next();
System.out.println(sdr.getDailyRecordId());
System.out.println(sdr.getDate());
}
[size=medium]输出如下:[/size]
Hibernate:
select ...from demo.stock
where stock0_.STOCK_ID=?
Hibernate:
select ...from demo.stock_daily_record
where stockdaily0_.STOCK_ID=?
[size=medium][color=blue]2、@Fetch(FetchMode.JOIN)[/color]
“join”类型fetch策略将禁用延迟加载所有相关的集合。Hibernate通过在SELECT语句使用OUTER JOIN(外连接)来获得对象的关联实例或者关联集合。
执行上面的代码输出如下:[/size]
Hibernate:
select ...
from
demo.stock stock0_
left outer join
demo.stock_daily_record stockdaily1_
on stock0_.STOCK_ID=stockdaily1_.STOCK_ID
where
stock0_.STOCK_ID=?
[size=medium]Hibernate只生成一个select语句,当Stock被初始化时,它获取所有其相关的集合。
[color=blue]3、@Fetch(FetchMode.SUBSELECT)[/color]
Fetching策略能够将其所有相关集合放在一个子select语句中。
另外发送一条SELECT 语句抓取在前面查询到(或者抓取到)的所有实体对象的关联集合。除非你显式的指定lazy="false" 禁止延迟抓取(lazy fetching),否则只有当你真正访问关联关系的时候,才会执行第二条select语句。
当通过Query等接口查询多个实体时,如果指定fetch="subselect"则将通过子查询获取集合
还是运行上面代码输出:[/size]
Hibernate:
select ...
from demo.stock stock0_
Hibernate:
select ...
from
demo.stock_daily_record stockdaily0_
where
stockdaily0_.STOCK_ID in (
select
stock0_.STOCK_ID
from
demo.stock stock0_
)
[size=medium][color=blue]4、@BatchSize(size=10)[/color]
对查询抓取的优化方案,通过指定一个主键或外键列表,Hibernate使用单条SELECT语句获取一批对象实例或集合。
当通过Query等接口查询多个实体时,如果指定farm的batch-size="……"则将通过使用单条SELECT语句获取一批对象实例或集合。
可指定全局批量抓取策略: hibernate.default_batch_fetch_size,取值:建议的取值为4, 8, 和16。
执行上面代码输出如下:[/size]
Hibernate:
select ...from demo.stock
where stock0_.STOCK_ID=?
Hibernate:
select ...from demo.stock_daily_record
where stockdaily0_.STOCK_ID=?
[size=medium]batch-size什么也没做。请看下面的解释:
[color=red]batch-size fetching策略并不是定义集合中有多少记录被预加载。相反,它其实是定义有多少集合应该被加载。[/color]
[/size]
List list = session.createQuery("from Stock").list();
for(Stock stock : list){
Set sets = stock.getStockDailyRecords();
for ( Iterator iter = sets.iterator();iter.hasNext(); ) {
StockDailyRecord sdr = (StockDailyRecord) iter.next();
System.out.println(sdr.getDailyRecordId());
System.out.println(sdr.getDate());
}
}
[size=medium]没有batch-size fetching策略时,输出:[/size]
Hibernate:
select ...
from demo.stock stock0_
Hibernate:
select ...
from demo.stock_daily_record stockdaily0_
where stockdaily0_.STOCK_ID=?
Hibernate:
select ...
from demo.stock_daily_record stockdaily0_
where stockdaily0_.STOCK_ID=?
继续重复select语句....这取决于数据表中有多少条stock记录。
[size=medium]如果在数据库中有20条股票记录,Hibernate默认的fetching策略将生成20+1个select语句,这将对数据库造成一定的冲击。
启用batch-size=”10” fetching策略时,输出:
[/size]
Hibernate:
select ...
from demo.stock stock0_
Hibernate:
select ...
from demo.stock_daily_record stockdaily0_
where
stockdaily0_.STOCK_ID in (
?, ?, ?, ?, ?, ?, ?, ?, ?, ?
)
[size=medium]现在,Hibernate将预提取集合,使用select in语句。如果在数据库中有20条股票记录,Hibernate将生成3个select语句
[align=center][b]抓取优化[/b][/align]
集合N+1:
可以使用batch-size来减少获取次数,即如batch-size=”10”,则是N/10+1。
开启二级缓存。
对于集合比较小且一定会用到的可采用fetch=”join”,这样只需一条语句。
[align=center][b]结论[/b][/align]
Fetching策略非常具有弹性,是一个优化Hibernate查询的非常重要的技巧。不过如果用在错误的地方,那将会是一个灾难。[/size]