上一篇文章(Hibernate的延迟加载 ,懒加载,lazy)说到Hibernate的延迟加载跟fetch的配置还有一定关系,下面就来讨论下fetch的用法。
抓取策略(fetch)是指当我们去查询一个对象里面所关联的其他对象时,按照哪种方法去抓取关联对象。
fetch策略常用的有三种:select、subselect、join,下面我们一一介绍。我们还是用上面介绍延迟加载的相关表和实体类。
Company表:
Employee表(employee_company_id为外键)
Company实体类:import java.util.Set;
public class Company {
private int companyId;
private String companyName;
private Set companyEmployees;
public int getCompanyId() {
return companyId;
}
public void setCompanyId(int companyId) {
this.companyId = companyId;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
public Set getCompanyEmployees() {
return companyEmployees;
}
public void setCompanyEmployees(Set companyEmployees) {
this.companyEmployees = companyEmployees;
}
}
Employee实体类:public class Employee {
private int employeeId;
private String employeeName;
private Company employeeCompany;
public int getEmployeeId() {
return employeeId;
}
public void setEmployeeId(int employeeId) {
this.employeeId = employeeId;
}
public String getEmployeeName() {
return employeeName;
}
public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}
public Company getEmployeeCompany() {
return employeeCompany;
}
public void setEmployeeCompany(Company employeeCompany) {
this.employeeCompany = employeeCompany;
}
}
Company hbm配置:
Employee hbm配置:
foreign-key="fk_employee_company" column="employee_company_id"
cascade="save-update">
fetch属性用在、、、等关联标签里,我们就用来介绍,但subselect策略只能用于和。从数据库取数据就用get()方法,让外层对象不用延迟加载。
1. select
作用:查询关联对象时,是拿外层对象的外键,向数据库再发送select语句去抓取关联对象。
修改上面Employee hbm的配置:
foreign-key="fk_employee_company" column="employee_company_id"
cascade="save-update" fetch="select">
在标签中我们添加了fetch="select",下面是测试代码:Employee employee = (Employee)session.get(Employee.class, 1); //A
System.out.println(employee.getEmployeeName());
Company company = employee.getEmployeeCompany(); //B
System.out.println(company.getCompanyName()); //C
A:此处会生成sql语句去查询employee的信息。select
employee0_.employee_id as employee1_1_0_,
employee0_.employee_name as employee2_1_0_,
employee0_.employee_company_id as employee3_1_0_
from
employee employee0_
where
employee0_.employee_id=?
B:此处不会马上去取company的信息,因为里面默认是lazy="proxy"支持懒加载的。
C:此处会根据employee的外键去查找company的信息,用的就是select fetch策略。select
company0_.company_id as company_1_0_0_,
company0_.company_name as company_2_0_0_
from
company company0_
where
company0_.company_id=?
注意:等关联标签默认是fetch="select"的,所以我们不配置也会采用多条select语句的方式去查询关联对象。
2. join
作用:查询关联对象时,是拿外层对象的外键去join关联对象的表,把关联对象一起取出来。
修改上面Employee hbm的配置:
foreign-key="fk_employee_company" column="employee_company_id" cascade="save-update" fetch="join">
在标签中我们换成了fetch="join",下面是测试代码:Employee employee = (Employee)session.get(Employee.class, 1); //A
System.out.println(employee.getEmployeeName());
Company company = employee.getEmployeeCompany(); //B
System.out.println(company.getCompanyName()); //C
A:此处会生成sql语句去查询employee的信息,并用left join去查询company的信息。select
employee0_.employee_id as employee1_1_0_,
employee0_.employee_name as employee2_1_0_,
employee0_.employee_company_id as employee3_1_0_,
company1_.company_id as company_1_0_1_,
company1_.company_name as company_2_0_1_
from
employee employee0_
left outer join
company company1_
on
employee0_.employee_company_id=company1_.company_id
where
employee0_.employee_id=?
B、C:这两处都不会再生成sql去数据库查询了,因为上面已经通过join拿到了company的信息。
所以使用join策略的话,关联对象的延迟加载就没用了,因为第一个select语句就已经join查询出了关联对象的信息。
3. subselect
作用:
subselect策略只能用于和,所以用来说明。
我们先来看看在默认的select策略下的执行过程。测试代码如下:List allCompany = session.createCriteria(Company.class).list(); //A
for(Company company : allCompany){
for(Employee employee : company.getCompanyEmployees()){ //B
System.out.println(employee.getEmployeeName());
}
}
A:发出查询sql语句去查询所有的company信息select
this_.company_id as company_1_0_0_,
this_.company_name as company_2_0_0_
from
company this_
B:这里会根据company_id去查询关联的employee信息select
companyemp0_.employee_company_id as employee3_0_0_,
companyemp0_.employee_id as employee1_1_0_,
companyemp0_.employee_id as employee1_1_1_,
companyemp0_.employee_name as employee2_1_1_,
companyemp0_.employee_company_id as employee3_1_1_
from
employee companyemp0_
where
companyemp0_.employee_company_id=?
上面的sql会重复向数据库发送N次,有多少个company就发送多少次。如果有很多不同的company,这种方式肯定是不合适的,因为会向数据库发送很多重复的sql语句。
下面我们来看看subselect策略,先修改Company hbm配置:
测试代码跟上面一样:List allCompany = session.createCriteria(Company.class).list(); //A
for(Company company : allCompany){
for(Employee employee : company.getCompanyEmployees()){ //B
System.out.println(employee.getEmployeeName());
}
}
A:发出查询sql语句去查询所有的company信息select
this_.company_id as company_1_0_0_,
this_.company_name as company_2_0_0_
from
company this_
B:这里会用一个select子查询去查询所有的employee信息select
companyemp0_.employee_company_id as employee3_0_1_,
companyemp0_.employee_id as employee1_1_1_,
companyemp0_.employee_id as employee1_1_0_,
companyemp0_.employee_name as employee2_1_0_,
companyemp0_.employee_company_id as employee3_1_0_
from
employee companyemp0_
where
companyemp0_.employee_company_id in (
select
this_.company_id
from
company this_
)
上面的sql只会向数据库发一次,因为所有的employee都已经取出来了。
对于对于set来说,如果用于遍历,则使用subselect,这样可以一次性把所有数据取出来;如果只取set里几条数据就用默认的select,这样虽然会发送多次sql,但查询量较少。