Hibernate - >具有n:m关系的LazyInitializationException

时间:2010-10-12 12:33:16

标签: java hibernate lazy-initialization

我遇到了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>();

那么问题是什么?

4 个答案:

答案 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);