在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;
}
}
现在,当我们使用session get从数据库加载来自类Foo的对象时,例如:
Foo foo =(Foo)session.get(Foo.class,1 / *或DB * /中存在的其他一些id); foo的Bar成员是一个代理对象(在我们的例子中是javassist代理,但它可以是cglib,取决于你使用的字节码提供者),它没有被初始化。 如果然后使用session.get来获取Bar对象,该对象是刚刚加载的Foo类的成员(我们在同一个会话中),Hibernate不会发出另一个数据库查询并从会话(第一级)缓存中获取对象。问题是这是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类也是刚刚获取的Foo对象的惰性成员。
答案 0 :(得分:2)
我遇到了类似的问题:
为了避免这些Session问题,我总是在保存,合并,更新或删除后执行刷新和清除。必须解决这些讨厌的问题需要花费我太多的时间。
答案 1 :(得分:0)
没有真正看到这个问题,虽然我们确实得到了间歇性的Lazy Load错误 - 所以也许我们有同样的问题,无论如何,是否可以选择使用不同的会话来加载Bar对象 - 应该从中加载它划伤,我希望......
答案 2 :(得分:0)
我无法重现您所看到的行为。这是我的代码:
@Entity
public class Foo {
private Long id; private String name; private Bar bar;
public Foo() { }
public Foo(String name) { this.name = name; }
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
@Basic
public String getName() { return name; }
public void setName(String name) { this.name = name; }
@ManyToOne(fetch = FetchType.LAZY)
public Bar getBar() { return bar; }
public void setBar(Bar bar) { this.bar = bar; }
}
@Entity
public class Bar {
private Long id; private String name;
public Bar() { }
public Bar(String name) { this.name = name; }
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
@Basic
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
public void testGets() {
SessionFactory sf = new AnnotationConfiguration()
.addPackage("hibtest")
.addAnnotatedClass(Foo.class)
.addAnnotatedClass(Bar.class)
.configure().buildSessionFactory();
Session session = null;
Transaction txn = null;
// Create needed data
try {
session = sf.openSession();
txn = session.beginTransaction();
// Create a Bar
Bar bar = new Bar("Test Bar");
session.save(bar);
// Create a Foo
Foo foo = new Foo("Test Foo");
session.save(foo);
foo.setBar(bar);
txn.commit();
} catch (HibernateException ex) {
if (txn != null) txn.rollback();
throw ex;
} finally {
if (session != null) session.close();
}
// Try the fetch
try {
session = sf.openSession();
Foo foo = (Foo) session.get(Foo.class, 1L);
Bar bar = (Bar) session.get(Bar.class, 1L);
System.out.println(bar.getName());
} finally {
if (session != null) session.close();
}
}
这一切都很好,正如人们所期望的那样。
答案 3 :(得分:0)
你真的需要做懒人装吗? 您是否可以将FetchType设置为EAGER并始终使用连接(正确)加载?
答案 4 :(得分:0)
你做错了什么。我没有测试你的代码,但你永远不需要强制代理的初始化,属性访问器会为你做。如果您明确使用Hibernate,请不要介意使用JPA,因为您已经丢失了可移植性。
Hibernate应该在需要获取或写入db时自动检测。如果从代理发出getProperty(),则hibernate或任何其他jpa提供程序应该从db中获取相应的行。
我不确定hibernate唯一的情况是聪明,如果你发出一个save()然后发出一个带有已保存对象id的get(),可能会出现问题如果save()没有将对象刷新为db。