Hibernate很少有获取策略来优化Hibernate生成的select语句,从而使其尽可能高效。 在映射关系中声明了获取策略,以定义Hibernate如何获取其相关集合和实体。
提取策略
有四种获取策略
1. fetch-“ join” =禁用延迟加载,始终加载所有集合和实体。
2. fetch-“选择”(默认)=延迟加载所有集合和实体。
3. batch-size =“ N” =最多提取“ N”个集合或实体,*不记录*。
4. fetch-“ subselect” =将其集合分组为一个sub select语句。
有关详细说明,您可以查看Hibernate文档 。
提取策略示例
这是获取策略演示的“一对多关系”示例。 股票属于许多股票每日记录。
在XML文件中声明提取策略的示例
...
<hibernate-mapping>
<class name="com.mkyong.common.Stock" table="stock">
<set name="stockDailyRecords" cascade="all" inverse="true"
table="stock_daily_record" batch-size="10" fetch="select">
<key>
<column name="STOCK_ID" not-null="true" />
</key>
<one-to-many class="com.mkyong.common.StockDailyRecord" />
</set>
</class>
</hibernate-mapping>
在注释中声明获取策略的示例
...
@Entity
@Table(name = "stock", catalog = "mkyong")
public class Stock implements Serializable{
...
@OneToMany(fetch = FetchType.LAZY, mappedBy = "stock")
@Cascade(CascadeType.ALL)
@Fetch(FetchMode.SELECT)
@BatchSize(size = 10)
public Set<StockDailyRecord> getStockDailyRecords() {
return this.stockDailyRecords;
}
...
}
让我们探索获取策略如何影响Hibernate生成的SQL语句。
1. fetch =“ select”或@Fetch(FetchMode.SELECT)
这是默认的获取策略。 它启用了所有相关集合的延迟加载。 让我们看一个例子……
//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());
}
输出量
Hibernate:
select ...from mkyong.stock
where stock0_.STOCK_ID=?
Hibernate:
select ...from mkyong.stock_daily_record
where stockdaily0_.STOCK_ID=?
Hibernate生成了两个select语句
1.选择语句以检索库存记录– session.get(Stock.class,114)
2.选择与其相关的集合– sets.iterator()
2. fetch =“ join”或@Fetch(FetchMode.JOIN)
“ join”获取策略将禁用所有相关集合的延迟加载。 让我们看一个例子……
//call select from stock and stock_daily_record
Stock stock = (Stock)session.get(Stock.class, 114);
Set sets = stock.getStockDailyRecords();
//no extra select
for ( Iterator iter = sets.iterator();iter.hasNext(); ) {
StockDailyRecord sdr = (StockDailyRecord) iter.next();
System.out.println(sdr.getDailyRecordId());
System.out.println(sdr.getDate());
}
输出量
Hibernate:
select ...
from
mkyong.stock stock0_
left outer join
mkyong.stock_daily_record stockdaily1_
on stock0_.STOCK_ID=stockdaily1_.STOCK_ID
where
stock0_.STOCK_ID=?
Hibernate仅生成一个select语句,它在初始化Stock时检索其所有相关集合。 – session.get(Stock.class,114)
1.选择语句以检索“库存记录”,并将其相关集合外联。
3. batch-size =“ 10”或@BatchSize(size = 10)
许多Hibernate开发人员总是误解这种“批量”获取策略。 让我们在这里看到*误解*概念...
Stock stock = (Stock)session.get(Stock.class, 114);
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());
}
您的预期结果是什么,这是从集合中每次提取10条记录? 查看输出
输出量
Hibernate:
select ...from mkyong.stock
where stock0_.STOCK_ID=?
Hibernate:
select ...from mkyong.stock_daily_record
where stockdaily0_.STOCK_ID=?
批量大小在这里什么也没做,不是批量大小的工作方式。 请参阅此语句。
批量大小提取策略未定义在集合中装入多少记录。 相反,它定义了应加载的集合数量。
-重复N次,直到您记住该声明-
另一个例子
让我们看另一个示例,您想要一张一张地打印所有库存记录及其相关的库存每日记录(集合)。
List<Stock> 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());
}
}
没有批量提取策略
输出量
Hibernate:
select ...
from mkyong.stock stock0_
Hibernate:
select ...
from mkyong.stock_daily_record stockdaily0_
where stockdaily0_.STOCK_ID=?
Hibernate:
select ...
from mkyong.stock_daily_record stockdaily0_
where stockdaily0_.STOCK_ID=?
Keep repeat the select statements....depend how many stock records in your table.
如果数据库中有20条股票记录,则Hibernate的默认获取策略将生成20 + 1条select语句并命中数据库。
1.选择语句以检索所有库存记录。
2.选择与其相关的收藏
3.选择与其相关的收藏
4.选择与其相关的收藏
…。
21.选择与其相关的收藏
生成的查询效率不高,并导致严重的性能问题。
启用了batch-size = '10'的提取策略
让我们看另一个启用batch-size = '10'的示例。
输出量
Hibernate:
select ...
from mkyong.stock stock0_
Hibernate:
select ...
from mkyong.stock_daily_record stockdaily0_
where
stockdaily0_.STOCK_ID in (
?, ?, ?, ?, ?, ?, ?, ?, ?, ?
)
现在,Hibernate将使用select * in *语句来预取集合。 如果您有20条库存记录,它将生成3条选择语句。
1.选择语句以检索所有库存记录。
2.选择In语句以获取其相关集合(一次10个集合)
3.选择In语句以获取其相关集合(一次为下10个集合)
启用批处理大小后,它将select语句从21个select语句简化为3个select语句。
4. fetch =“ subselect”或@Fetch(FetchMode.SUBSELECT)
此提取策略可在子select语句中启用其所有相关集合。 让我们再次看到相同的查询。
List<Stock> 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());
}
}
输出量
Hibernate:
select ...
from mkyong.stock stock0_
Hibernate:
select ...
from
mkyong.stock_daily_record stockdaily0_
where
stockdaily0_.STOCK_ID in (
select
stock0_.STOCK_ID
from
mkyong.stock stock0_
)
启用“ subselect”后,它将创建两个select语句。
1.选择语句以检索所有库存记录。
2.在子选择查询中选择其所有相关集合。
结论
提取策略具有高度的灵活性,并且是优化Hibernate查询的非常重要的调整,但是如果在错误的位置使用它,将是完全的灾难。
参考
1. http://docs.jboss.org/hibernate/core/3.3/reference/en/html/performance.html
2. https://www.hibernate.org/315.html
翻译自: https://mkyong.com/hibernate/hibernate-fetching-strategies-examples/