类级别的检索策略java
类级别的检索策略能够经过class元素的lazy属性来设置,该属性默认为true,即为延迟检索。sql
延迟检索只对load方式有效,对get和query的查询无效。数据库
延迟减速简单来讲就是经过CGLIB动态生成代理,加载时仅加载对象的OID属性,在调用该对象的其余属性时,代理像数据库发送sql语句加载对象,测试一下:session
<class name="cn.net.bysoft.model1.Customer" table="CUSTOMERS" lazy="true">
@Test public void testLazyIsTrue() { // 在延迟加载的状况下load一个customer对象。 // 由hibernate生成代理,只初始化customer的id属性。 Customer customer = (Customer) session.load(Customer.class, 1); // 使用id的操做不会发送sql语句到数据库。 System.out.println("使用id属性以前不会发送sql语句去数据库查询," + customer.getId()); System.out.println("==================="); // 使用其余属性时,在发送sql数据。 System.out.println("使用name属性以前会发送sql语句去数据库查询," + customer.getName()); }
若是将lazy属性设置为false,则在load方法调用时,当即发送sql语句到数据库查询对象,关闭延迟检索,也叫当即检索,进行一个测试:测试
<class name="cn.net.bysoft.model1.Customer" table="CUSTOMERS" lazy="false">
@Test public void testLazyIsFalse() { // 当即检索costomer对象,在load时候发送sql语句到数据库进行查询。 Customer customer = (Customer) session.load(Customer.class, 1); System.out.println(customer.getId()); System.out.println(customer.getName()); }
一对多、多对多的检索策略fetch
一对多与多对多的检索策略有三个属性:lazy、batch-size和fetch。
spa
在配置文件中,对set元素设置lazy属性能够用来决定set的初始化方式,该属性能够为true、false和extra。首先来看看lazy等于true。.net
在加载对象时,对象中若存在集合属性,也是默认进行延迟加载的(lazy=true),也就是说在不使用集合对象时,hibernate不会发送sql语句去数据库查询,测试一下: hibernate
<!-- customer.hbm.xml配置文件中的set元素 --> <set name="orders" table="ORDERS" inverse="true" cascade="delete" lazy="true">
@Test public void testOntToManyLazyIsTrue() { Customer customer = (Customer)session.get(Customer.class, 1); Set<Order> order = customer.getOrders(); // 在不使用集合中的对象时,将不会发送sql语句到数据库去查询order。 System.out.println(order.getClass()); }
设置lazy=false则会在加载customer时也发送sql语句去初始化order集合。代理
<!-- customer.hbm.xml配置文件中的set元素 --> <set name="orders" table="ORDERS" inverse="true" cascade="delete" lazy="false">
@Test public void testOntToManyLazyIsFalse() { // 发送查询customer和order的sql语句。 Customer customer = (Customer) session.get(Customer.class, 1); }
将lazy设置成extra能够加强延迟检索,好比,在lazy等于true时,调用集合的size()方法也会发送查询集合数据的sql语句,而设置成extran后,在使用集合的某些方法时,将尽量的发送合理的sql语句,也就是说能不初始化集合就不初始化集合。测试一下:
<!-- customer.hbm.xml配置文件中的set元素 --> <set name="orders" table="ORDERS" inverse="true" cascade="delete" lazy="extra">
@Test public void testOntToManyLazyIsExtra() { Customer customer = (Customer) session.get(Customer.class, 1); Set<Order> orders = customer.getOrders(); // 使用集合的size属性时,sql语句只发送select count(id) from ...查询总行数,而不会查询所有order数据。 System.out.println(orders.size()); }
接下来看看batch-size的做用,该属性会设定批量检索的数量,用代码来理解一下,首先不设置batch-size,便利每一个customer中的order数量,在for循环中,每个getOrders().size()都会发送一次sql语句,下面是代码和数据库中的测试数据,第一张图是customer表,第二张图是order表:
<set name="orders" table="ORDERS" inverse="true" cascade="delete" lazy="true">
@Test public void testBatchSize() { // 查询全部的customer List<Customer> customers = session.createQuery("from Customer c") .list(); System.out.println(customers.size()); // 循环得到每一个customer的orders。 // 在不设置batchsize的状况下每次都会发送sql语句到数据库去查询。 for (Customer customer : customers) { if (customer.getOrders() != null) { System.out.println(customer.getOrders().size()); } } }
显然在for循环中,每次调用.size()都发送sql语句到数据库查询不是最优的办法,这是,batch-size就有用武之地了。能够看到,测试数据中,每个customer都有3个订单,那么将batch-size设置成3,将使用in(?,?,?)的sql语句去数据库查询,in中的id数量就是batch-size设置的值,结果只发送一条sql就能够了:
<set name="orders" table="ORDERS" inverse="true" cascade="delete" lazy="true" batch-size="3">
接下来就是fetch属性了,该属性有三个值,分别是:select、subselect和join。该属性用于设置集合的联合查询方式。
其中select和subselect能够会决定集合的查询形式,上面在作batch-size的测试时,因为没有设置fetch属性,fetch的默认值是select,查询order表的sql语句是... in (customerid1, customerid2, customerid3),如今将fetch的属性改成subselect在看看会输出什么样的sql语句。
<set name="orders" table="ORDERS" inverse="true" cascade="delete" lazy="true" batch-size="3" fetch="subselect">
设置成subselect能够发现初始化集合时的条件由普通的查询条件变为了子查询方式,这就是fetch属性的做用,注意,在subselect模式下,咱们设置的batch-size属性无效。
还有一个join,设置后hibernate在初始化集合时会采用“迫切左外链接”的方式,在该模式下,设置的lazy属性无效,可是hql查询会忽略fetch=join,依旧使用lazy加载策略。
进行一个测试:
<set name="orders" table="ORDERS" inverse="true" cascade="delete" lazy="true" batch-size="3" fetch="join">
@Test public void testFetchJoin() { // 设置了fetch等于join后,采用迫切左外链接查询 // 会经过left join一次查找出全部的customer和它的orders // 忽略lazy属性。 Customer customer = (Customer) session.get(Customer.class, 1); Set<Order> orders = customer.getOrders(); System.out.println(orders.size()); }
多对一和一对一的检索策略
和set元素同样,many-to-one也有lazy属性和fetch属性。lazy属性有三个值,分别是:proxy、no-proxy和false。proxy表明延迟加载,false表明当即加载。fetch属性有两个值,分别是:select和join。select表明普通的抓取方式,join表明使用迫切左外链接的抓取方式。
使用join将忽略lazy=proxy。
进行一下测试,得到一个order,默认状况下,不会对order对应的customer进行检索,记得把customer配置文件的lazy设置成true:
<!-- order.hbm.xml的多对一配置 --> <many-to-one name="customer" class="cn.net.bysoft.model1.Customer" column="CUSTOMER_ID"> </many-to-one>
@Test public void testManyToOne() { // 查询一个order对象 Order order = (Order) session.get(Order.class, 1); System.out.println(order.getName()); }
当设置lazy=false时,会使用当即检索得到order对应的customer的数据。
<!-- order.hbm.xml的多对一配置 --> <many-to-one name="customer" class="cn.net.bysoft.model1.Customer" column="CUSTOMER_ID" lazy="false"> </many-to-one>
将fetch设置成join,结果以下:
<!-- order.hbm.xml的多对一配置 --> <many-to-one name="customer" class="cn.net.bysoft.model1.Customer" column="CUSTOMER_ID" lazy="false" fetch="join">
最后,在来看一个问题,在测试一对多的查询检索时作过的,若是将全部的order都查询出来,使用循环来得到每个order的customer会怎么样呢?
@Test public void testManyToOneBatchSize() { // 查询一个order对象 List<Order> orders = session.createQuery("from Order o").list(); for(Order order : orders) { System.out.println(order.getCustomer().getName()); } }
因为测试数据中customer表有4条记录,因此能够看到打印了4个sql语句,那么可否只发送一个sql语句查询出这4个customer呢?能够,在customer.hbm.xml中的class元素设置batch-size属性就能作到了。
<class name="cn.net.bysoft.model1.Customer" table="CUSTOMERS" batch-size="4">
以上,就是hibernate检索策略的简单说明。