I have a Java EE project and the MySQL database is managed with an ORM. I worked a lot with Hibernate to learn what I am doing wrong and I think I understand session/transaction, but I do not know how I can solve this in my case/architecture.
I have a Project and a Person which are in a bi-directional n:m relation with a join table. The Project is the mapping-owner. Now I want to delete a Person which is connected to a Project.
So I thought, I can do this:
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);
But I get an error in this line:
Iterator<Project> iterator = projects.iterator();
But it seems it has to do with:
Person person = findPersonById(personId);
and:
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;
}
}
Here I make a transaction and only get the person object, but no corresponding projects. And the iterator will try to get them, but can not, because my transaction/session is closed.
This is because of my architecture:
I have:
IPersonService -> IPersonImplementation -> IPersonDao -> IPersonDaoImplementation
and only the method in IPersonDaoImplementation opens and closes the transaction. So in my ServicePersonImplementation I can not do several DB related stuff (I do not want to have FetchType.EAGER in my @ManyToMany annotation, because this would load to much not needed stuff). Because once it returns from my DaoImplementation I can not perform other actions on the returned person (in my case get all projects which are combined with this person).
So what can I do in my case?
Best Regards Tim.
Update:
PersonDaoImpl, remove method:
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();
}
Trace of the error:
ERROR: org.hibernate.LazyInitializationException – failed to lazily initialize a collection of role: com.testdomain.testproject.domain.Person.projects, no session or session was closed
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.testdomain.testproject.domain.Person.projects, no session or session was closed
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:383)
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:375)
at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:368)
at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:111)
at org.hibernate.collection.PersistentSet.iterator(PersistentSet.java:186)
at com.testdomain.testproject.dao.impl.PersonDaoImplHibernate.removePerson(PersonDaoImplHibernate.java:71)
at com.testdomain.testproject.service.impl.PersonServiceImpl.removePerson(PersonServiceImpl.java:88)
at 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)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:46)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Failed deleting Person: failed to lazily initialize a collection of role: com.testdomain.testproject.domain.Person.projects, no session or session was closed
What you really want to do is push all of the Person removal code down into the PersonDao’s remove() method. Inside that method, you want to do the following:
In JPA, you shouldn’t need to do anything more than that, however I believe in straight Hibernate you may need to
saveOrUpdatethen changes made to each Project as well as remove the Person.So your code might look something like this:
(Note: this is roughly closer to pseudo code and has not been tested inside an actual IDE)