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

类级别的检索策略

    类级别的检索策略可以通过class元素的lazy属性来设置,该属性默认为true,即为延迟检索。

    延迟检索只对load方式有效,对get和query的查询无效。

    延迟减速简单来说就是通过CGLIB动态生成代理,加载时仅加载对象的OID属性,在调用该对象的其他属性时,代理像数据库发送sql语句加载对象,测试一下:

<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());
    }

223716_phYS_2450666.png

    如果将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());
    }

224112_83eY_2450666.png

一对多、多对多的检索策略

    一对多与多对多的检索策略有三个属性:lazy、batch-size和fetch。

    在配置文件中,对set元素设置lazy属性可以用来决定set的初始化方式,该属性可以为true、false和extra。首先来看看lazy等于true。

    在加载对象时,对象中若存在集合属性,也是默认进行延迟加载的(lazy=true),也就是说在不使用集合对象时,hibernate不会发送sql语句去数据库查询,测试一下:   

<!-- 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());
    }

230758_P3V3_2450666.png

    设置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);
    }

231938_c7ze_2450666.png

    将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());
    }

232821_uLEB_2450666.png

    接下来看看batch-size的作用,该属性会设定批量检索的数量,用代码来理解一下,首先不设置batch-size,便利每个customer中的order数量,在for循环中,每一个getOrders().size()都会发送一次sql语句,下面是代码和数据库中的测试数据,第一张图是customer表,第二张图是order表:

234507_vO3I_2450666.png

234508_EEqr_2450666.png

<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());
            }
        }
    }

234805_SBnG_2450666.png

    显然在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">

234944_2A1V_2450666.png

    接下来就是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">

000402_xYtH_2450666.png

    设置成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());
    }

001136_gSmC_2450666.png

多对一和一对一的检索策略

    和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());
    }

003535_8zuA_2450666.png

    当设置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>

003829_4AAt_2450666.png

    将fetch设置成join,结果如下:

<!-- order.hbm.xml的多对一配置 -->
        <many-to-one name="customer" class="cn.net.bysoft.model1.Customer"
            column="CUSTOMER_ID" lazy="false" fetch="join">

003938_ts9P_2450666.png

    最后,在来看一个问题,在测试一对多的查询检索时做过的,如果将所有的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());
        }
    }

004342_JEL5_2450666.png

    由于测试数据中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">

004609_66SS_2450666.png

    以上,就是hibernate检索策略的简单说明。

转载于:https://my.oschina.net/u/2450666/blog/656404

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值