国产chinesehdxxxx野外,国产av无码专区亚洲av琪琪,播放男人添女人下边视频,成人国产精品一区二区免费看,chinese丰满人妻videos

Hibernate:檢索策略的學(xué)習(xí)1

2018-09-28 14:25 更新

概述


檢索數(shù)據(jù),也就是查詢數(shù)據(jù)是在一個(gè)系統(tǒng)中必不可少的一個(gè)功能。檢索數(shù)據(jù)時(shí)的2個(gè)問(wèn)題:

  1. 不浪費(fèi)內(nèi)存:例如,Customer和Order是雙向1-N的關(guān)系。當(dāng) Hibernate 從數(shù)據(jù)庫(kù)中加載 Customer 對(duì)象時(shí), 如果同時(shí)加載所有關(guān)聯(lián)的 Order 對(duì)象, 而程序?qū)嶋H上僅僅需要訪問(wèn) Customer 對(duì)象, 那么這些關(guān)聯(lián)的 Order 對(duì)象就白白浪費(fèi)了許多內(nèi)存。
  2. 更高的查詢效率:發(fā)送盡可能少的 SQL 語(yǔ)句。

由于篇幅原因,將內(nèi)容分為了兩部分:

Hibernate:檢索策略的學(xué)習(xí)1、Hibernate:檢索策略的學(xué)習(xí)2

第一部分講解了類級(jí)別的檢索策略以及1-N和N-N的檢索策略,在第二部分中將學(xué)習(xí)N-1和1-1的檢索策略,并對(duì)檢索策略進(jìn)行總結(jié)。

類級(jí)別的檢索策略


知識(shí)點(diǎn)

類級(jí)別可選的檢索策略包括立即檢索和延遲檢索, 默認(rèn)為延遲檢索。

  • 立即檢索: 立即加載檢索方法指定的對(duì)象;
  • 延遲檢索: 延遲加載檢索方法指定的對(duì)象。在使用具體的屬性時(shí),再進(jìn)行加載。

類級(jí)別的檢索策略可以通過(guò) 元素的 lazy 屬性進(jìn)行設(shè)置。

如果程序加載一個(gè)對(duì)象的目的是為了訪問(wèn)它的屬性,可以采取立即檢索;如果程序加載一個(gè)持久化對(duì)象的目的是僅僅為了獲得它的引用,可以采用延遲檢索,但需要注意懶加載異常(LazyInitializationException:簡(jiǎn)單理解該異常就是Hibernate在使用延遲加載時(shí),并沒(méi)有將數(shù)據(jù)實(shí)際查詢出來(lái),而只是得到了一個(gè)代理對(duì)象,當(dāng)使用屬性的時(shí)候才會(huì)去查詢,而如果這個(gè)時(shí)候session關(guān)閉了,則會(huì)報(bào)該異常)!

下面通過(guò)一個(gè)例子來(lái)進(jìn)行講解:

Demo

在該Demo中,我們只需要使用一個(gè)Customer的對(duì)象即可,其中包含了id,name等屬性。

延遲檢索

首先我們來(lái)測(cè)試一下元素的lazy屬性為true的情況,也就是默認(rèn)情況(不設(shè)置)。

public class HibernateTest {

    private SessionFactory sessionFactory;
    private Session session;
    private Transaction transaction;
    @Before
    public void init() {
        Configuration configuration = new Configuration().configure();
        ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
                .applySettings(configuration.getProperties())
                .buildServiceRegistry();
        sessionFactory = configuration.buildSessionFactory(serviceRegistry);

        session = sessionFactory.openSession();
        transaction = session.beginTransaction();
    }
    @After
    public void destroy() {
        transaction.commit();
        session.close();
        sessionFactory.close();
    }
    @Test
    public void testClassLevelStrategy() {
        Customer customer = (Customer) session.load(Customer.class, 1);
        System.out.println(customer.getClass());
        System.out.println(customer.getCustomerId());
        System.out.println(customer.getCustomerName());
    }
}

看一下上面的代碼,該代碼是利用Junit進(jìn)行測(cè)試(關(guān)于Junit的知識(shí)在這不多說(shuō),并不是重點(diǎn))。其中init方法是對(duì)SessionFactory、Session等進(jìn)行初始化,destroy方法是進(jìn)行關(guān)閉。

當(dāng)我們運(yùn)行testClassLevelStrategy()方法時(shí),會(huì)得到以下輸出結(jié)果:

class com.atguigu.hibernate.strategy.Customer_$$_javassist_1
1
Hibernate:
    select
        customer0_.CUSTOMER_ID as CUSTOMER1_0_0_,
        customer0_.CUSTOMER_NAME as CUSTOMER2_0_0_
    from
        CUSTOMERS customer0_
    where
        customer0_.CUSTOMER_ID=?
AA

通過(guò)控制臺(tái)的輸出結(jié)果,我們可以清楚的看到,當(dāng)我們執(zhí)行session.load(Customer.class, 1)方法時(shí),Hibernate并沒(méi)有發(fā)送SQL語(yǔ)句,而只是返回了一個(gè)對(duì)象,通過(guò)調(diào)用getClass()方法,可以看到該對(duì)象class com.atguigu.hibernate.strategy.Customer_$$_javassist_1是一個(gè)代理對(duì)象,并且當(dāng)調(diào)用customer.getCustomerId()獲取ID的時(shí)候,也沒(méi)有發(fā)送SQL語(yǔ)句;當(dāng)我們這個(gè)再調(diào)用customer.getCustomerName()方法來(lái)得到name的時(shí)候,這個(gè)時(shí)候才發(fā)送了SQL語(yǔ)句進(jìn)行真正的查詢,并且WHERE條件中帶上的就是ID。

如果我們?cè)?code>System.out.println(customer.getCustomerName());語(yǔ)句前插入session.close()將Session關(guān)閉,就能看到之前上文中提到的懶加載異常。

立即檢索

為了讓Customer類可以立即檢索,我們要修改Customer.hbm.xml文件,在標(biāo)簽中加入lazy="false"。

<hibernate-mapping package="com.atguigu.hibernate.strategy">

    <class name="Customer" table="CUSTOMERS" lazy="false">

        <id name="customerId" type="java.lang.Integer">
            <column name="CUSTOMER_ID" />
            <generator class="native" />
        </id>
    ...

這個(gè)時(shí)候,我們?cè)龠\(yùn)行之前的單元測(cè)試代碼,控制臺(tái)會(huì)得到以下輸出結(jié)果:

Hibernate:
    select
        customer0_.CUSTOMER_ID as CUSTOMER1_0_0_,
        customer0_.CUSTOMER_NAME as CUSTOMER2_0_0_
    from
        CUSTOMERS customer0_
    where
        customer0_.CUSTOMER_ID=?
class com.atguigu.hibernate.strategy.Customer
1
AA

我們可以看到,當(dāng)調(diào)用load方法時(shí),會(huì)發(fā)送SQL語(yǔ)句,并且得到的不再是代理對(duì)象。這個(gè)時(shí)候就算我們?cè)谳敵鰊ame屬性之前將session關(guān)閉,也不會(huì)報(bào)錯(cuò)。

小結(jié)

上文中對(duì)Hibernate的類級(jí)別的檢索策略進(jìn)行了總結(jié),當(dāng)然這些是建立在有一定基礎(chǔ)的前提下。需要注意的是:

  • 無(wú)論元素的lazy 屬性是true 還是false,Session 的get() 方法及Query 的list() 方法在類級(jí)別總是使用立即檢索策略。
  • 若 元素的 lazy 屬性為 true 或取默認(rèn)值, Session 的 load() 方法不會(huì)執(zhí)行查詢數(shù)據(jù)表的 SELECT 語(yǔ)句,僅返回代理類對(duì)象的實(shí)例,該代理類實(shí)例有如下特征:
    • 由 Hibernate 在運(yùn)行時(shí)采用 CGLIB 工具動(dòng)態(tài)生成;
    • Hibernate 創(chuàng)建代理類實(shí)例時(shí),僅初始化其 OID 屬性;
    • 在應(yīng)用程序第一次訪問(wèn)代理類實(shí)例的非 OID 屬性時(shí), Hibernate 會(huì)初始化代理類實(shí)例。

1-N和N-N的檢索策略


知識(shí)點(diǎn)

我在之前的博客中對(duì)1-N和N-N有過(guò)學(xué)習(xí),所以我假設(shè)讀者已經(jīng)了解了Hibernate中關(guān)于1-N和N-N的映射。我們建立了Customer與Order的1-N關(guān)聯(lián)關(guān)系,表示一個(gè)顧客可以有多個(gè)訂單。

我們?cè)谟成湮募?,用元素?lái)配置1-N關(guān)聯(lián)以及N-N關(guān)聯(lián)關(guān)系。元素有l(wèi)azy、fetch和batch-size屬性。

  • lazy: 主要決定orders 集合被初始化的時(shí)機(jī)。即到底是在加載Customer 對(duì)象時(shí)就被初始化, 還是在程序訪問(wèn) orders 集合時(shí)被初始化。
  • fetch: 取值為 “select” 或 “subselect” 時(shí), 決定初始化 orders 的查詢語(yǔ)句的形式;若取值為”join”, 則決定 orders 集合被初始化的時(shí)機(jī)
  • 若把 fetch 設(shè)置為 “join”, lazy 屬性將被忽略
  • 元素的 batch-size 屬性:用來(lái)為延遲檢索策略或立即檢索策略設(shè)定批量檢索的數(shù)量. 批量檢索能減少 SELECT 語(yǔ)句的數(shù)目, 提高延遲檢索或立即檢索的運(yùn)行性能。
lazy屬性
(默認(rèn)值true)
fetch屬性
(默認(rèn)值select)
檢索策略
true未設(shè)置
(取默認(rèn)值select)
采用延遲檢索策略,這是默認(rèn)的檢索策略,也是優(yōu)先考慮使用的檢索策略
false未設(shè)置
(取默認(rèn)值select)
采用立即索策略,當(dāng)使用Hibernate二級(jí)緩存時(shí)可以考慮使用立即檢索
extra未設(shè)置
(取默認(rèn)值select)
采用加強(qiáng)延遲檢索策略,它盡可能的延遲orders集合被初始化的時(shí)機(jī)
true,extra
or extra
未設(shè)置
(取默認(rèn)值select)
lazy屬性決定采用的檢索策略,即決定初始化orders集合的時(shí)機(jī)。fetch屬性為select,意味
著通過(guò)select語(yǔ)句來(lái)初始化orders的集合,形式為SELECT * FROM orders WHERE customer
_id IN (1,2,3,4)
true,extra
or extra
subselectlazy屬性決定采用的檢索策略,即決定初始化orders集合的時(shí)機(jī)。fetch屬性為subselect,意味
著通過(guò)subselect語(yǔ)句來(lái)初始化orders的集合,形式為SELECT * FROM orders WHERE
customer_id IN (SELECT id FROM customers)
truejoin采采用迫切左外連接策略

Lazy

我們現(xiàn)在開始研究一下關(guān)于元素的lazy屬性。

Demo

首先我們看一下延遲檢索,也就是屬性的lazy為true或者不設(shè)置的情況下:

@Test
public void testOne2ManyLevelStrategy() {
        Customer customer = (Customer) session.get(Customer.class, 1);
        System.out.println(customer.getCustomerName());
    System.out.println(customer.getOrders().getClass());
        System.out.println(customer.getOrders().size());
}

下面是控制的輸出結(jié)果

Hibernate:
    select
        customer0_.CUSTOMER_ID as CUSTOMER1_0_0_,
        customer0_.CUSTOMER_NAME as CUSTOMER2_0_0_
    from
        CUSTOMERS customer0_
    where
        customer0_.CUSTOMER_ID=?
AA
class org.hibernate.collection.internal.PersistentSet
Hibernate:
    select
        orders0_.CUSTOMER_ID as CUSTOMER3_0_1_,
        orders0_.ORDER_ID as ORDER_ID1_1_1_,
        orders0_.ORDER_ID as ORDER_ID1_1_0_,
        orders0_.ORDER_NAME as ORDER_NA2_1_0_,
        orders0_.CUSTOMER_ID as CUSTOMER3_1_0_
    from
        ORDERS orders0_
    where
        orders0_.CUSTOMER_ID=?
3

從結(jié)果中可以明顯的看出,Hibernate使用了延遲檢索。其中的orders并沒(méi)有初始化,而是返回了一個(gè)集合代理對(duì)象。當(dāng)我們通過(guò)customer.getOrders().size()這段代碼真正要使用orders集合的時(shí)候,才發(fā)送SQL語(yǔ)句進(jìn)行查詢。

在延遲檢索(lazy屬性值為true)集合屬性時(shí),Hibernate在以下情況下初始化集合代理類實(shí)例:

  • 應(yīng)用程序第一次訪問(wèn)集合屬性: iterator(), size(), isEmpty(), contains() 等方法
  • 通過(guò) Hibernate.initialize() 靜態(tài)方法顯式初始化

下面我們將的lazy屬性修改為false,如<set name="orders" table="ORDERS" inverse="true" lazy="false">。

修改完之后再執(zhí)行測(cè)試代碼,輸出結(jié)果也就很明顯了,在調(diào)用load()方法時(shí),會(huì)先執(zhí)行SQL語(yǔ)句取出Customer以及相關(guān)聯(lián)的orders。

最后,提一下lazy的另一個(gè)取值extra。該取值與true類似,主要區(qū)別是增強(qiáng)延遲檢索策略能夠進(jìn)一步延遲Customer對(duì)象的orders集合代理實(shí)例的初始化時(shí)機(jī)。

首先我們將元素中的lazy設(shè)為extra。我們同樣的執(zhí)行上文中的單元測(cè)試代碼, 得到以下結(jié)果:

Hibernate:
    select
        customer0_.CUSTOMER_ID as CUSTOMER1_0_0_,
        customer0_.CUSTOMER_NAME as CUSTOMER2_0_0_
    from
        CUSTOMERS customer0_
    where
        customer0_.CUSTOMER_ID=?
AA
class org.hibernate.collection.internal.PersistentSet
Hibernate:
    select
        count(ORDER_ID)
    from
        ORDERS
    where
        CUSTOMER_ID =?
3

我們觀察第二個(gè)SQL語(yǔ)句。我們發(fā)現(xiàn)他并沒(méi)有對(duì)orders進(jìn)行初始化,而是通過(guò)使用一個(gè)count()函數(shù)。extra取值為增強(qiáng)的延遲檢索,該取值會(huì)盡可能的延遲集合初始化的時(shí)機(jī)。

例如:當(dāng)我們將lazy設(shè)置為true(延遲檢索),而我們調(diào)用order.size()方法的時(shí)候,這個(gè)時(shí)候就會(huì)通過(guò)SQL將orders集合初始化。但現(xiàn)在我們用extra這個(gè)屬性,發(fā)現(xiàn)我們調(diào)用orders的size方法,并沒(méi)有初始化,而是通過(guò)了一個(gè)count函數(shù)。

所以我們得到結(jié)論:

增強(qiáng)延遲檢索策略能進(jìn)一步延遲 Customer 對(duì)象的 orders 集合代理實(shí)例的初始化時(shí)機(jī):

  • 當(dāng)程序第一次訪問(wèn) order 屬性的 size(), contains() 和 isEmpty() 方法時(shí), Hibernate 不會(huì)初始化 orders 集合類的實(shí)例, 僅通過(guò)特定的 select 語(yǔ)句查詢必要的信息, 不會(huì)檢索所有的 Order 對(duì)象。
  • 當(dāng)程序第一次訪問(wèn) orders 屬性的 iterator() 方法時(shí), 會(huì)導(dǎo)致 orders 集合代理類實(shí)例的初始化。(這個(gè)是沒(méi)辦法的= =)

但其實(shí)我們?cè)趯?shí)際的開發(fā)過(guò)程中,當(dāng)我們要用到size或者contains等方法的時(shí)候,基本上代表我們就要用到集合部分的屬性。如果我們選用extra的話,反倒會(huì)多發(fā)送SQL語(yǔ)句。

關(guān)于extra的其他點(diǎn)大家可以自己進(jìn)行一些測(cè)試,比較簡(jiǎn)單方便。

BatchSize

元素有一個(gè)batch-size 屬性,用來(lái)為延遲檢索策略或立即檢索策略設(shè)定批量檢索的數(shù)量。批量檢索能減少 SELECT 語(yǔ)句的數(shù)目,提高延遲檢索或立即檢索的運(yùn)行性能。### Demo首先說(shuō)一下這個(gè)Demo中用到的數(shù)據(jù)的數(shù)據(jù),Customers表中共有4條數(shù)據(jù)。而我們的lazy屬性設(shè)為true,即采用延遲加載策略。我們看一下下面的代碼:```java@Testpublic void testSetBatchSize() { List customers = session.createQuery("FROM Customer").list(); System.out.println(customers.size()); for (Customer customer : customers) { if (customer.getOrders() != null) System.out.println(customer.getOrders().size()); }}```首先,在該代碼的第三行我們獲取了所有的Customer,得到list集合(集合中存的是Customer對(duì)象),然后遍歷該list集合;每次循環(huán),我們都通過(guò)customer.getOrders方法來(lái)實(shí)例化orders集合。篇幅原因,我在這不貼控制臺(tái)的輸出了。但可以想到的是,會(huì)一共發(fā)出5條SQL。第一條是獲取customer集合,因?yàn)榧嫌?個(gè)customer對(duì)象,所以會(huì)再發(fā)出4條SQL來(lái)分別初始化。好了,現(xiàn)在我們修改元素,在里邊加入`batch-size="4"`,所以現(xiàn)在的元素的代碼為``。我們重新運(yùn)行單元測(cè)試代碼,得到結(jié)果:```Hibernate: select customer0_.CUSTOMER_ID as CUSTOMER1_0_, customer0_.CUSTOMER_NAME as CUSTOMER2_0_ from CUSTOMERS customer0_4Hibernate: select orders0_.CUSTOMER_ID as CUSTOMER3_0_1_, orders0_.ORDER_ID as ORDER_ID1_1_1_, orders0_.ORDER_ID as ORDER_ID1_1_0_, orders0_.ORDER_NAME as ORDER_NA2_1_0_, orders0_.CUSTOMER_ID as CUSTOMER3_1_0_ from ORDERS orders0_ where orders0_.CUSTOMER_ID in ( ?, ?, ?, ? )3330```我們看到這個(gè)時(shí)候只有2條SQL語(yǔ)句。第一條同樣的還是得到customer,而第二條SQL語(yǔ)句直接全部初始化了4個(gè)orders集合。這就是batch-size的作用所在。我們可以想到的是,如果我們將batch-size設(shè)為2,則是3條SQL語(yǔ)句。**也就是我們上文中提到的批量檢索。**## Fetch元素的fetch屬性是用于確定初始化orders 集合的方式。1. 默認(rèn)值為orders,也就是通過(guò)正常的方式來(lái)初始化set元素。例如我們?cè)趯atch-size例子的時(shí)候,我們并沒(méi)有設(shè)置fetch屬性(默認(rèn)即為select),所以我們?cè)诔跏蓟痮rders集合的時(shí)候,會(huì)發(fā)現(xiàn)在SQL語(yǔ)句中是通過(guò)`where orders0_.CUSTOMER_ID in (?, ?, ?, ?)`這種方式來(lái)進(jìn)行初始化的。2. 可以取值為subselect,我們看名字也能知道大概意思,就是通過(guò)子查詢的方式來(lái)初始化所有的set集合。例如我們?cè)O(shè)置為subselect后,可看到SQL語(yǔ)句中包含`where orders0_.CUSTOMER_ID in (select customer0_.CUSTOMER_ID from CUSTOMERS customer0_)`。**子查詢作為WHERE子句的in的條件出現(xiàn)的,子查詢查詢1的一端的ID,此時(shí)lazy屬性是有效的,但batch-size屬性失效。**3. 若取值為join: + 在加載1的一端的對(duì)象的時(shí)候,使用迫切左外連接(可參考該博客進(jìn)行學(xué)習(xí)[Hibernate:深入HQL學(xué)習(xí)](http://tracylihui.github.io/2015/07/08/Hibernate%EF%BC%9A%E6%B7%B1%E5%85%A5HQL%E5%AD%A6%E4%B9%A0/))的方式檢索N的一端的集合的屬性; + 忽略lazy屬性(理解了迫切左外連接,也就能知道為啥會(huì)忽略lazy屬性了); + HQL查詢忽略`fetch="join"`的取值; + **Query 的list() 方法會(huì)忽略映射文件中配置的迫切左外連接檢索策略, 而依舊采用延遲加載策略。(這個(gè)點(diǎn)之前測(cè)試的時(shí)候老是忽略掉了)**# 總結(jié)以上就是關(guān)于Hibernate檢索策略的學(xué)習(xí),但并不全。將在下一篇中,對(duì)N-1和1-1的檢索策略進(jìn)行學(xué)習(xí),并做一個(gè)總結(jié)。
以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)