简体   繁体   English

在Hibernate组件标签中,延迟加载不起作用

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

I have a small hibernate application as above: 我有一个小的hibernate应用程序,如上所示: AAAA

BankAccount class is as follows: 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
    }

Address class is as below: 地址类如下:

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 file is as below: 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 file is as below: 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>

Now my question is in BankAccount.hbm.xml file in the component tag I am using using lazy="true", when ever I am firing select query on BankAccount class using session.get(BankAccount.class, 1235); 现在我的问题是在我使用lazy =“true”的组件标签中的BankAccount.hbm.xml文件中,当我使用session.get(BankAccount.class, 1235);在BankAccount类上触发select查询时session.get(BankAccount.class, 1235); It is loading address details also from database, the code I used to fire select query is below: 它也是从数据库加载地址的详细信息,我用来触发选择查询的代码如下:

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

The query fired is 触发的查询是

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=?

But I am expecting address details will be loaded lazily from database when ever I used bankAccount.getAddress() method only? 但是我希望只使用bankAccount.getAddress()方法时,地址详细信息将从数据库中bankAccount.getAddress()加载?

Now can any one please explain why hibernate is loading address details eagerly, and how to load then lazily? 现在任何人都可以解释为什么hibernate急切地加载地址细节,以及如何加载懒惰?

Take an example from below code:- 以下代码为例: -

class B {  
    private C cee;  

    public C getCee() {  
        return cee;  
    }  

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

class C {  
    // Not important really  
}  

Right after loading B, you may call getCee() to obtain C. But look, getCee() is a method of your class and Hibernate has no control over it. 在加载B之后,你可以调用getCee()来获得C.但是看看, getCee()是你的类的一个方法,而Hibernate无法控制它。 Hibernate does not know when someone is going to call getCee() . Hibernate不知道有人打算调用getCee() That means Hibernate must put an appropriate value into " cee " property at the moment it loads B from database. 这意味着Hibernate必须在从数据库加载B时将适当的值放入“ cee ”属性。

If proxy is enabled for C, Hibernate can put a C-proxy object which is not loaded yet, but will be loaded when someone uses it. 如果为C启用了代理,Hibernate可以放置一个尚未加载的C-proxy对象,但是当有人使用它时将加载它。 This gives lazy loading for one-to-one. 这为一对一提供了延迟加载。

But now imagine your B object may or may not have associated C (constrained="false") . 但现在想象你的B对象可能有也可能没有关联的C(约束=“假”) What should getCee() return when specific B does not have C? 当特定B没有C时, getCee()应该返回什么? Null. 空值。 But remember, Hibernate must set correct value of "cee" at the moment it set B (because it does no know when someone will call getCee() ). 但请记住,Hibernate必须在设置B时设置正确的“cee”值(因为它不知道有人何时会调用getCee() )。 Proxy does not help here because proxy itself in already non-null object. 代理在这里没有帮助,因为代理本身已经是非null对象。

If your B->C mapping is mandatory (constrained=true) , Hibernate will use proxy for C resulting in lazy initialization. 如果你的B-> C映射是强制性的(约束=真) ,Hibernate将使用C代理导致延迟初始化。 But if you allow B without C, Hibernate just HAS TO check presence of C at the moment it loads B. But a SELECT to check presence is just inefficient because the same SELECT may not just check presence, but load entire object. 但是如果你允许B没有C,那么Hibernate只是在它加载B时检查C的存在。但是检查存在的SELECT效率很低,因为相同的SELECT可能不仅仅检查存在,而是加载整个对象。 So lazy loading goes away. 懒惰的装载消失了。

Workaround1 : - Just add annotation or entry in hdm file for @JoinColumn for reference private Address address; 解决方法1 : - 只需在hdm文件中为@JoinColumn添加注释或条目,以获取引用的private Address address; .

Workaround2 :- add optional=false in OneToOne relationship 解决方法2 : - 在OneToOne关系中添加optional = false

Other solutions for this problem: 此问题的其他解决方案:

The simplest one is to fake one-to-many relationship. 最简单的就是伪造一对多的关系。 This will work because lazy loading of collection is much easier then lazy loading of single nullable property but generally this solution is very inconvenient if you use complex JPQL/HQL queries. 这将起作用,因为延迟加载集合比单个可空属性的延迟加载容易得多,但是如果使用复杂的JPQL / HQL查询,通常这种解决方案非常不方便。

The other one is to use build time bytecode instrumentation. 另一种是使用构建时间字节码检测。 For more details please read Hibernate documentation: 19.1.7. 有关更多详细信息,请阅读Hibernate文档:19.1.7。 Using lazy property fetching. 使用lazy属性获取。 Remember that in this case you have to add @LazyToOne(LazyToOneOption.NO_PROXY) annotation to one-to-one relationship to make it lazy. 请记住,在这种情况下,您必须将@LazyToOne(LazyToOneOption.NO_PROXY)注释添加到一对一关系中以使其变得懒惰。 Setting fetch to LAZY is not enough. 将提取设置为LAZY是不够的。

The last solution is to use runtime bytecode instrumentation but it will work only for those who use Hibernate as JPA provider in full-blown Java EE environment (in such case setting " hibernate.ejb.use_class_enhancer " to true should do the trick: Entity Manager Configuration) or use Hibernate with Spring configured to do runtime weaving (this might be hard to achieve on some older application servers). 最后一个解决方案是使用运行时字节码检测,但它只适用于那些在完整的Java EE环境中使用Hibernate作为JPA提供者的人(在这种情况下,将“ hibernate.ejb.use_class_enhancer ”设置为true应该可以做到这一点:实体管理器配置)或使用配置了Spring的Hibernate进行运行时编织(这在某些较旧的应用程序服务器上可能很难实现)。 In this case @LazyToOne(LazyToOneOption.NO_PROXY) annotation is also required. 在这种情况下,还需要@LazyToOne(LazyToOneOption.NO_PROXY)注释。

This will work for you. 这对你有用。

Hibernate does not create proxies for components, that's why lazy loading does not work for them. Hibernate不会为组件创建代理,这就是为什么延迟加载对它们不起作用的原因。

Solutions: 解决方案:

  1. Use bytecode instrumentation to enable lazy loading of non-entity fields. 使用字节码检测来启用非实体字段的延迟加载。 It has its own pitfalls and is not widely adopted. 它有自己的陷阱,并没有被广泛采用。
  2. Use two different classes for BankAccount , one containing the Address component (as it is now), and one without it, and map them to the same table. BankAccount使用两个不同的类,一个包含Address组件(现在是),一个没有它,并将它们映射到同一个表。 Then, use the one without address in contexts in which you don't need addresses. 然后,在不需要地址的上下文中使用没有地址的那个。
  3. Use fake one-to-one association between BankAccount and Address by making Address component an entity and mapping it to the same table. 通过将Address组件作为实体并将其映射到同一个表,在BankAccountAddress之间使用假的一对一关联。 The drawback here is that you must not insert the Address instances (because you'll end up trying to insert a separate row in the table), but rather you have to read and update it after you insert the corresponding BankAccount entity instance. 这里的缺点是您不能插入Address实例(因为您最终会尝试在表中插入单独的行),而是在插入相应的BankAccount实体实例后必须读取并更新它。
  4. Change the db schema and move the component to its own separate table. 更改数据库架构并将组件移动到其自己的单独表中。 Then simply promote the component to an entity and map it to the new table. 然后只需将组件提升为实体并将其映射到新表。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM