類級別的檢索策略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檢索策略的簡單說明。