我有一个小的hibernate应用程序,如上所示:
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急切地加载地址详细信息,以及如何加载懒惰?
答案 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不会为组件创建代理,这就是为什么延迟加载对它们不起作用的原因。
解决方案:
BankAccount
使用两个不同的类,一个包含Address
组件(现在是),一个没有它,并将它们映射到同一个表。然后,在您不需要地址的环境中使用没有地址的那个。BankAccount
组件作为实体并将其映射到同一个表,在Address
和Address
之间使用fake one-to-one关联。这里的缺点是您不能插入Address
实例(因为您最终会尝试在表中插入单独的行),而是在插入相应的内容后必须读取并更新它BankAccount
实体实例。