簡體   English   中英

在Hibernate組件標簽中,延遲加載不起作用

[英]In Hibernate Component tag lazy loading is not working

我有一個小的hibernate應用程序,如上所示: AAAA

BankAccount類如下:

    package in.co.way2learn;
    import java.util.Set;
    public class BankAccount {
         private int accountNumber;
         private String accountHoldersName;
         private int balance;
         private Address address;
         private Set<String> emails;
         //setters and getters
    }

地址類如下:

package in.co.way2learn;

public class Address {
    private String addressLine1;
    private String addressLine2;
    private String city;
    private String country;
    private int pinCode;

    //setters and getters
}

BankAccount.hbm.xml文件如下:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated Jul 2, 2014 3:59:34 PM by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping package="in.co.way2learn">
    <class name="BankAccount">
        <id name="accountNumber" type="integer">
            <generator class="assigned"/>
        </id>
        <property name="accountHoldersName" type="string"/>
        <property name="balance" type="integer"/>
        <component name="address" class="Address" lazy="true"> 
            <property name="addressLine1"/>
            <property name="addressLine2"/>
            <property name="city"/>
            <property name="country"/>
            <property name="pinCode"/>
        </component>
        <set name="emails" order-by="email asc" table="bankaccount_emails">
            <key column="SNo"/>
            <element column="email" type="string"/>
        </set>
    </class>
</hibernate-mapping>

hibernate.cfg.xml文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">
            org.gjt.mm.mysql.Driver
        </property>
        <property name="hibernate.connection.password">root</property>
        <property name="hibernate.connection.url">
            jdbc:mysql://localhost:3306/way2learnDB
        </property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.dialect">
            org.hibernate.dialect.MySQLInnoDBDialect
        </property>
        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.format_sql">true</property>
        <property name="hibernate.hbm2ddl.auto">update</property>

        <mapping resource="in/co/way2learn/BankAccount.hbm.xml"/>
    </session-factory>
</hibernate-configuration>

現在我的問題是在我使用lazy =“true”的組件標簽中的BankAccount.hbm.xml文件中,當我使用session.get(BankAccount.class, 1235);在BankAccount類上觸發select查詢時session.get(BankAccount.class, 1235); 它也是從數據庫加載地址的詳細信息,我用來觸發選擇查詢的代碼如下:

Session session=sessionFactory.openSession();
Transaction transaction=session.beginTransaction();
BankAccount bankAccount=(BankAccount)session.get(BankAccount.class, 1235);
transaction.commit();
session.close();

觸發的查詢是

Hibernate: 
    select
        bankaccoun0_.accountNumber as accountN1_0_0_,
        bankaccoun0_.accountHoldersName as accountH2_0_0_,
        bankaccoun0_.balance as balance3_0_0_,
        bankaccoun0_.addressLine1 as addressL4_0_0_,
        bankaccoun0_.addressLine2 as addressL5_0_0_,
        bankaccoun0_.city as city6_0_0_,
        bankaccoun0_.country as country7_0_0_,
        bankaccoun0_.pinCode as pinCode8_0_0_ 
    from
        BankAccount bankaccoun0_ 
    where
        bankaccoun0_.accountNumber=?

但是我希望只使用bankAccount.getAddress()方法時,地址詳細信息將從數據庫中bankAccount.getAddress()加載?

現在任何人都可以解釋為什么hibernate急切地加載地址細節,以及如何加載懶惰?

以下代碼為例: -

class B {  
    private C cee;  

    public C getCee() {  
        return cee;  
    }  

    public void setCee(C cee) {  
        this.cee = cee;  
    }  
}  

class C {  
    // Not important really  
}  

在加載B之后,你可以調用getCee()來獲得C.但是看看, getCee()是你的類的一個方法,而Hibernate無法控制它。 Hibernate不知道有人打算調用getCee() 這意味着Hibernate必須在從數據庫加載B時將適當的值放入“ cee ”屬性。

如果為C啟用了代理,Hibernate可以放置一個尚未加載的C-proxy對象,但是當有人使用它時將加載它。 這為一對一提供了延遲加載。

但現在想象你的B對象可能有也可能沒有關聯的C(約束=“假”) 當特定B沒有C時, getCee()應該返回什么? 空值。 但請記住,Hibernate必須在設置B時設置正確的“cee”值(因為它不知道有人何時會調用getCee() )。 代理在這里沒有幫助,因為代理本身已經是非null對象。

如果你的B-> C映射是強制性的(約束=真) ,Hibernate將使用C代理導致延遲初始化。 但是如果你允許B沒有C,那么Hibernate只是在它加載B時檢查C的存在。但是檢查存在的SELECT效率很低,因為相同的SELECT可能不僅僅檢查存在,而是加載整個對象。 懶惰的裝載消失了。

解決方法1 : - 只需在hdm文件中為@JoinColumn添加注釋或條目,以獲取引用的private Address address;

解決方法2 : - 在OneToOne關系中添加optional = false

此問題的其他解決方案:

最簡單的就是偽造一對多的關系。 這將起作用,因為延遲加載集合比單個可空屬性的延遲加載容易得多,但是如果使用復雜的JPQL / HQL查詢,通常這種解決方案非常不方便。

另一種是使用構建時間字節碼檢測。 有關更多詳細信息,請閱讀Hibernate文檔:19.1.7。 使用lazy屬性獲取。 請記住,在這種情況下,您必須將@LazyToOne(LazyToOneOption.NO_PROXY)注釋添加到一對一關系中以使其變得懶惰。 將提取設置為LAZY是不夠的。

最后一個解決方案是使用運行時字節碼檢測,但它只適用於那些在完整的Java EE環境中使用Hibernate作為JPA提供者的人(在這種情況下,將“ hibernate.ejb.use_class_enhancer ”設置為true應該可以做到這一點:實體管理器配置)或使用配置了Spring的Hibernate進行運行時編織(這在某些較舊的應用程序服務器上可能很難實現)。 在這種情況下,還需要@LazyToOne(LazyToOneOption.NO_PROXY)注釋。

這對你有用。

Hibernate不會為組件創建代理,這就是為什么延遲加載對它們不起作用的原因。

解決方案:

  1. 使用字節碼檢測來啟用非實體字段的延遲加載。 它有自己的陷阱,並沒有被廣泛采用。
  2. BankAccount使用兩個不同的類,一個包含Address組件(現在是),一個沒有它,並將它們映射到同一個表。 然后,在不需要地址的上下文中使用沒有地址的那個。
  3. 通過將Address組件作為實體並將其映射到同一個表,在BankAccountAddress之間使用假的一對一關聯。 這里的缺點是您不能插入Address實例(因為您最終會嘗試在表中插入單獨的行),而是在插入相應的BankAccount實體實例后必須讀取並更新它。
  4. 更改數據庫架構並將組件移動到其自己的單獨表中。 然后只需將組件提升為實體並將其映射到新表。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM