在 Hibernate 中,我们有两个类,其中包含以下具有 JPA 映射的类:
package com.example.hibernate
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
@Entity
public class Foo {
private long id;
private Bar bar;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
@ManyToOne(fetch = FetchType.LAZY)
public Bar getBar() {
return bar;
}
public void setBar(Bar bar) {
this.bar = bar;
}
}
package com.example.hibernate
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
public class Bar {
private long id;
private String title;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
现在,当我们使用会话从数据库加载类 Foo 的对象时,例如:
Foo foo = (Foo)session.get(Foo.class, 1 /* or some other id that exists in the DB*/); foo 的 Bar 成员是一个代理对象(在我们的例子中是 javassist 代理,但它可以是 cglib 之一,具体取决于您使用的字节码提供程序),它没有被初始化。如果您随后使用 session.get 获取 Bar 对象,它是刚刚加载的 Foo 类的成员(我们在同一个会话中),Hibernate 不会发出另一个 DB 查询并从会话(第一级)缓存中获取对象. 问题是这是未初始化的 Bar 类的代理,尝试调用此对象 getId() 将返回 0,而 getTitle() 将返回 null。我们当前的解决方案非常难看,它检查从 get 返回的对象是否是代理这里是代码(形成一个通用的 DAO 实现):
@SuppressWarnings("unchecked")
@Override
@Transactional(readOnly = true)
public <T extends IEntity> T get(Class<T> clazz, Serializable primaryKey) throws DataAccessException {
T entity = (T) currentSession().get(clazz, primaryKey);
if (entity != null) {
if (LOG.isWarnEnabled()) {
LOG.warn("Object not found for class " + clazz.getName() + " with primary key " + primaryKey);
}
} else if (entity instanceof HibernateProxy){ // TODO: force initialization due to Hibernate bug
HibernateProxy proxy = (HibernateProxy)entity;
if (!Hibernate.isInitialized(proxy)) {
Hibernate.initialize(proxy);
}
entity = (T)proxy.getHibernateLazyInitializer().getImplementation();
}
return entity;
}
有没有更好的方法来做到这一点,在 Hibernate 论坛中找不到解决方案,在 Hibernate 的 JIRA 中也没有找到问题。
注意:我们不能只使用 foo.getBar() (它将正确初始化代理)来获取 Bar 类对象,因为获取 Bar 对象的 session.get 操作不知道(或不关心)Bar class 也是刚刚获取的 Foo 对象的惰性成员。