學習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檢索策略的簡單說明。

相關文章
相關標籤/搜索