学习hibernate(七) -- hibernate检索策略

类级别的检索策略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检索策略的简单说明。

相关文章
相关标签/搜索