我遇到了Hibernate和LazyInitializationException的问题。我搜索并找到了很多答案,但我不能用它们来解决我的问题,因为我不得不说,我是Hibernate的新手。
我运行JUnit测试,如果出现错误,请执行以下操作:
@Test
public void testAddPerson() {
Set<Person> persons = service.getAllPersons();
// create new person
Person person = new Person();
person.setEmail("john@doe.com");
Project testProject = serviceProj.findProjectById(1);
HashSet<Project> lister = new HashSet<Project>();
lister.add(testProject);
person.setProjects(lister);
service.addPerson(person);
testProject.getPersons().add(person);
...
}
最后显示的一行:
testProject.getPersons().add(person);
抛出此错误:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.doe.john.domain.Project.persons, no session or session was closed
人和项目是双向的n:m:
Person.java :
@ManyToMany(mappedBy="persons")
private Set<Project> projects = new HashSet<Project>();
Project.java :
@ManyToMany
@JoinTable(name = "Project_Person",
joinColumns = {@JoinColumn(name="project_id", referencedColumnName="id")},
inverseJoinColumns = {@JoinColumn(name="person_id", referencedColumnName="id")}
)
private Set<Person> persons = new HashSet<Person>();
那么问题是什么?
答案 0 :(得分:1)
问题是默认情况下该集合是延迟加载的。这意味着在访问它之前,它实际上不会从数据库加载。要加载它,您需要一个活动的会话/事务。
简单的方法是将其更改为FethType.EAGER,以确保立即填充集合。
- 更新 -
我最近遇到了同样的问题,最后我修改了我的实际服务来处理这类事情。在ProjectService类中声明一个addPerson方法。
public void addPersonTo(Project project, Person person)
{
project = em.merge(project); //EntityManager, not sure what you are using but you get the idea hopefully
project.addPerson(person);
}
答案 1 :(得分:1)
将您的代码放入交易中 - 这将为您解决问题
答案 2 :(得分:0)
你能试试吗
person.getProjects().Add(testProject)
而不是
HashSet<Project> lister = new HashSet<Project>();
lister.add(testProject);
person.setProjects(lister);
你应该这样做,否则你会吹掉一个hibernate托管集合。
答案 3 :(得分:0)
来自hibernate参考:
默认情况下,Hibernate3对集合使用延迟选择提取,对单值关联使用延迟代理提取。这些默认值对大多数应用程序中的大多数关联都有意义。
如果设置hibernate.default_batch_fetch_size,Hibernate将使用批量提取优化来进行延迟提取。此优化也可以在更细粒度的级别启用。
请注意,在打开的Hibernate会话的上下文之外访问延迟关联将导致异常。例如:
s = sessions.openSession();
Transaction tx = s.beginTransaction();
User u = (User) s.createQuery("from User u where u.name=:userName")
.setString("userName", userName).uniqueResult();
Map permissions = u.getPermissions();
tx.commit();
s.close();
Integer accessLevel = (Integer) permissions.get("accounts"); // Error!
由于在关闭会话时未初始化权限集合,因此集合将无法加载其状态。 Hibernate不支持对分离对象进行延迟初始化。这可以通过将从集合中读取的代码移动到提交事务之前来解决。
或者,您可以通过为关联映射指定lazy =“false”来使用非延迟集合或关联。但是,延迟初始化应用于几乎所有集合和关联。如果在对象模型中定义了太多非惰性关联,Hibernate将在每次事务中将整个数据库提取到内存中。
另一方面,您可以使用本质上非延迟的连接提取,而不是在特定事务中选择提取。我们现在将解释如何自定义提取策略。在Hibernate3中,选择获取策略的机制对于单值关联和集合是相同的。
所以你必须在访问集合后关闭会话!
而不是:
service.addPerson(person);
testProject.getPersons().add(person);
我认为你应该:
testProject.getPersons().add(person);
service.addPerson(person);