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

时间:2014-07-04 12:41:31

标签: java hibernate

我有一个小的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>

现在我的问题是在我正在使用的组件标记中的BankAccount.hbm.xml文件中使用lazy =&#34; true&#34;,当我使用session.get(BankAccount.class, 1235);在BankAccount类上触发选择查询时正在从数据库加载地址详细信息,我用于触发选择查询的代码如下:

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()方法时,地址详细信息会从数据库中懒散加载?

现在可以解释为什么hibernate急切地加载地址详细信息,以及如何加载懒惰?

2 个答案:

答案 0 :(得分:1)

从以下代码中获取示例: -

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必须将适当的值放入&#34; cee &#34;它从数据库加载B时的属性。

如果为C启用了代理,Hibernate可以放置一个尚未加载的C-proxy对象,但会在有人使用它时加载。这为一对一提供了延迟加载。

但现在想象你的B对象可能有也可能没有关联C(约束=&#34;假&#34;)。当特定B没有C时,getCee()应该返回什么?空值。但请记住,Hibernate必须设置正确的值&#34; cee&#34;目前它设置了B(因为它不知道有人何时会调用getCee())。代理在这里没有帮助,因为代理本身已经是非空对象。

如果您的B-> C映射是必需的(约束=真),Hibernate将使用C代理导致延迟初始化。但是如果你允许B没有C,那么Hibernate只是在它加载B时检查C的存在。但是检查存在的SELECT效率很低,因为相同的SELECT可能不仅仅检查存在,而是加载整个对象。懒惰的装载消失了。

解决方法1 : - 只需在hdm文件中为@JoinColumn添加注释或条目,以便参考private Address address;

解决方法2 : - 在OneToOne关系中添加可选= false

此问题的其他解决方案:

最简单的就是伪造一对多的关系。这将起作用,因为延迟加载集合比单个可空属性的延迟加载容易得多,但是如果使用复杂的JPQL / HQL查询,通常这种解决方案非常不方便。

另一个是使用构建时字节码检测。有关更多详细信息,请阅读Hibernate文档:19.1.7。使用lazy属性获取。请记住,在这种情况下,您必须将@LazyToOne(LazyToOneOption.NO_PROXY)注释添加到一对一关系中以使其变得懒惰。将提取设置为LAZY是不够的。

最后一个解决方案是使用运行时字节码检测,但它仅适用于在完整的Java EE环境中使用Hibernate作为JPA提供程序的用户(在这种情况下设置&#34; hibernate.ejb.use_class_enhancer&#34;如果真的应该这样做:实体管理器配置)或使用Hibernate与Spring配置来进行运行时编织(这可能很难在一些较旧的应用服务器上实现)。在这种情况下,还需要@LazyToOne(LazyToOneOption.NO_PROXY)注释。

这对你有用。

答案 1 :(得分:1)

Hibernate不会为组件创建代理,这就是为什么延迟加载对它们不起作用的原因。

解决方案:

  1. 使用字节码检测来启用非实体字段的延迟加载。它有自己的陷阱,并没有被广泛采用。
  2. BankAccount使用两个不同的类,一个包含Address组件(现在是),一个没有它,并将它们映射到同一个表。然后,在您不需要地址的环境中使用没有地址的那个。
  3. 通过将BankAccount组件作为实体并将其映射到同一个表,在AddressAddress之间使用fake one-to-one关联。这里的缺点是您不能插入Address实例(因为您最终会尝试在表中插入单独的行),而是在插入相应的内容后必须读取并更新它BankAccount实体实例。
  4. 更改数据库架构并将组件移动到其自己的单独表中。然后只需将组件提升为实体并将其映射到新表。