上一篇文章(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<Employee> 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<Employee> getCompanyEmployees() { return companyEmployees; } public void setCompanyEmployees(Set<Employee> 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配置:
<hibernate-mapping> <class name="com.jaeger.hibernatetest.day7.lazy.Company" table="company"> <id name="companyId" column="company_id"> <generator class="native"></generator> </id> <property name="companyName" column="company_name"/> <set name="companyEmployees" cascade="all"> <key column="employee_company_id"></key> <one-to-many class="com.jaeger.hibernatetest.day7.lazy.Employee"/> </set> </class> </hibernate-mapping>
Employee hbm配置:
<hibernate-mapping> <class name="com.jaeger.hibernatetest.day7.lazy.Employee" table="employee"> <id name="employeeId" column="employee_id"> <generator class="native"></generator> </id> <property name="employeeName" column="employee_name"/> <many-to-one name="employeeCompany" class="com.jaeger.hibernatetest.day7.lazy.Company" foreign-key="fk_employee_company" column="employee_company_id" cascade="save-update"> </many-to-one> </class> </hibernate-mapping>
fetch属性用在<many-to-one>、<one-to-many>、<many-to-many>、<one-to-one>等关联标签里,我们就用<many-to-one>来介绍,但subselect策略只能用于<one-to-many>和<many-to-many>。从数据库取数据就用get()方法,让外层对象不用延迟加载。
1. select
作用:查询关联对象时,是拿外层对象的外键,向数据库再发送select语句去抓取关联对象。
修改上面Employee hbm的配置:
<many-to-one name="employeeCompany" class="com.jaeger.hibernatetest.day7.lazy.Company" foreign-key="fk_employee_company" column="employee_company_id" cascade="save-update" fetch="select"> </many-to-one>
在<many-to-one>标签中我们添加了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的信息,因为<many-to-one>里面默认是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=?
注意:<many-to-one><one-to-many>等关联标签默认是fetch="select"的,所以我们不配置也会采用多条select语句的方式去查询关联对象。
2. join
作用:查询关联对象时,是拿外层对象的外键去join关联对象的表,把关联对象一起取出来。
修改上面Employee hbm的配置:
<many-to-one name="employeeCompany" class="com.jaeger.hibernatetest.day7.lazy.Company" foreign-key="fk_employee_company" column="employee_company_id" cascade="save-update" fetch="join"> </many-to-one>
在<many-to-one>标签中我们换成了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策略只能用于<one-to-many>和<many-to-many>,所以用<one-to-many>来说明。
我们先来看看在默认的select策略下的执行过程。测试代码如下:
List<Company> 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配置:
<set name="companyEmployees" cascade="all" fetch="subselect"> <key column="employee_company_id"></key> <one-to-many class="com.jaeger.hibernatetest.day7.lazy.Employee" /> </set>
测试代码跟上面一样:
List<Company> 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,但查询量较少。