Hibernate-> lazyinitialization takeception with n:m关系
-
29-09-2019 - |
题
我在冬眠和懒惰化诊断方面有问题。我搜索并找到了很多答案,但是我不能用它们来解决我的问题,因为我不得不说,我是冬眠的新手。
我运行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>();
所以有什么问题?
解决方案
问题是默认情况下,集合被懒洋洋地加载。这意味着它实际上不会从数据库加载,直到访问它为止。为了加载它,您将需要一个活动的会话/事务。
简单的出路是将其更改为fethtype.eager。
- 更新 -
我最近遇到了同样的问题,最终我修改了实际服务来处理这种事情。在您的ProjectsService类中声明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);
}
其他提示
将您的代码放入交易中 - 这将为您解决问题
你能试一下吗
person.getProjects().Add(testProject)
代替
HashSet<Project> lister = new HashSet<Project>();
lister.add(testProject);
person.setProjects(lister);
您应该这样做,因为否则您将吹走冬眠的托管收藏。
来自Hibernate参考:
默认情况下,Hibernate3使用懒惰的选择获取来收集和懒惰的代理获取单值关联。这些默认值对于大多数应用程序中的大多数关联是有意义的。
如果设置Hibernate.default_batch_fetch_size,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将在每个事务中将整个数据库获取到内存中。
另一方面,您可以使用Join Fetching,这是非自然而非懒惰的,而不是在特定交易中选择获取。现在,我们将解释如何自定义获取策略。在Hibernate3中,选择提取策略的机制对于单价值关联和集合是相同的。
因此,您必须在访问集合后关闭会话!
代替 :
service.addPerson(person);
testProject.getPersons().add(person);
我认为您应该有:
testProject.getPersons().add(person);
service.addPerson(person);