JPA(Hibernate) - 会话/事务和延迟加载

时间:2010-10-14 13:23:13

标签: java hibernate lazy-loading

我有一个Java EE项目,MySQL数据库是用ORM管理的。我在Hibernate上做了很多工作来了解我做错了什么,我认为我理解会话/事务,但我不知道如何在我的案例/架构中解决这个问题。

我有一个Project和一个Person,它与一个连接表是双向的n:m关系。该项目是地图所有者。现在我想删除一个连接到Project的Person。

所以我想,我可以这样做:

Person person = findPersonById(personId);
Set<Project> projects = person.getProjects();
Iterator<Project> iterator = projects.iterator();
while (iterator.hasNext()) {
    Project project = iterator.next();
    if (project.getPersons().contains(person)) {
        project.getPersons().remove(person);
        projectDao.updateProject(project);
    }
}
personDao.removePerson(personId);

但我在这一行中收到错误:

Iterator<Project> iterator = projects.iterator();

但它似乎与:

有关
Person person = findPersonById(personId);

public Person findPersonById(int personId) {
    SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
    Session sess = sessionFactory.getCurrentSession();

    Transaction tx = sess.beginTransaction();
    try {
        Person person = (Person)sess.createQuery("from Person where id = "+personId).list().get(0);
        tx.commit();
        return person;
    }
    catch (IndexOutOfBoundsException ex) {
        return null;
    }
}

这里我创建一个事务并且只获取person对象,但是没有相应的项目。并且迭代器将尝试获取它们,但不能,因为我的事务/会话已关闭。 这是因为我的架构:

我有: IPersonService - &gt; IPersonImplementation - &gt; IPersonDao - &gt; IPersonDaoImplementation 并且只有IPersonDaoImplementation中的方法才能打开和关闭事务。所以在我的ServicePersonImplementation中我不能做几个与DB相关的东西(我不想在我的@ManyToMany注释中使用FetchType.EAGER,因为这会加载到很多不需要的东西)。因为一旦它从我的DaoImplementation返回,我就无法对返回的人执行其他操作(在我的情况下,获取与此人合并的所有项目)。

那么在我的情况下我能做些什么呢?

最诚挚的问候蒂姆。

更新: PersonDaoImpl,删除方法:

public void removePerson(int personId){
    Person p = findPersonById(personId);

    SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
    Session sess = sessionFactory.getCurrentSession();

    Transaction tx = sess.beginTransaction();
    sess.delete(p);
    sess.flush();
    tx.commit();
}

跟踪错误:

错误:org.hibernate.LazyInitializationException - 无法懒惰地初始化角色集合:com.testdomain.testproject.domain.Person.projects,没有关闭会话或会话 org.hibernate.LazyInitializationException:无法懒惰地初始化角色集合:com.testdomain.testproject.domain.Person.projects,没有会话或会话被关闭     at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:383)     at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:375)     在org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:368)     在org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:111)     在org.hibernate.collection.PersistentSet.iterator(PersistentSet.java:186)     在com.testdomain.testproject.dao.impl.PersonDaoImplHibernate.removePerson(PersonDaoImplHibernate.java:71)     在com.testdomain.testproject.service.impl.PersonServiceImpl.removePerson(PersonServiceImpl.java:88)     在com.testdomain.testproject.service.PersonTest.testRemovePerson(PersonTest.java:180)     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)     在java.lang.reflect.Method.invoke(Method.java:597)     在org.junit.runners.model.FrameworkMethod $ 1.runReflectiveCall(FrameworkMethod.java:44)     在org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)     在org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)     在org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)     在org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)     在org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)     在org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)     在org.junit.runners.ParentRunner $ 3.run(ParentRunner.java:193)     在org.junit.runners.ParentRunner $ 1.schedule(ParentRunner.java:52)     在org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)     在org.junit.runners.ParentRunner.access $ 000(ParentRunner.java:42)     在org.junit.runners.ParentRunner $ 2.evaluate(ParentRunner.java:184)     在org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)     在org.junit.runners.ParentRunner.run(ParentRunner.java:236)     在org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:46)     在org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)     在org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)     在org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)     在org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)     在org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197) 删除Person失败:懒得初始化角色集合:com.testdomain.testproject.domain.Person.projects,没有关闭会话或会话

2 个答案:

答案 0 :(得分:6)

您真正想要做的是将所有Person删除代码推送到PersonDao的remove()方法中。在该方法中,您需要执行以下操作:

  • 开始交易
  • 按ID
  • 查找人员实例
  • 获取其项目集并将其从所有项目中删除
  • 删除Person实例本身
  • 提交交易

在JPA中,你不应该做更多的事情,但我相信你可能需要saveOrUpdate直接Hibernate,然后对每个项目进行更改以及删除Person。

所以你的代码看起来像这样:

public void removePerson(int personId) {
  SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
  Session sess = sessionFactory.getCurrentSession();
  Transaction tx = sess.beginTransaction();

  try {
    Person person = (Person)sess.createQuery("from Person where id = "+personId).list().get(0);
    Set<Project> projects = person.getProjects();
    for (Project project : projects) {
      if (project.getPersons().contains(person)) {
        project.getPersons().remove(person);
        sess.saveOrUpdate(project);
      }
    }

    sess.delete(person);
    tx.commit();
  }  catch (Exception e) {
     System.err.println("Failed deleting Person: "+e.getMessage());
  }
}

(注意:这大致更接近伪代码,并且尚未在实际的IDE中进行测试)

答案 1 :(得分:1)

你真的有两种选择。第一个是在服务/ DAO实现中执行此操作。

projectDao.removePersonFrom(Person personToRemove, Project projectToRemoveFrom);

personDao.removeProjectFrom(Project projectToRemove, Person personToRemoveFrom);

我通常有一个与视图相关联的Model类,有时候我会在事务中使用某些方法(我使用Spring)来做这种事情。

- 编辑 -

在daos中创建会话/事务,然后您可以执行失败的代码。您发布的代码应该可以正常工作。