I am using JPA in a J2SE application ("resource-local"), and I'm facing the following problem.
Given the following entities (a Project consists of a list of Employees):
Code:
@Entity
public class Project {
@Id
@Column(name="PROJECT_ID", nullable = false)
private long id;
@OneToMany(mappedBy="project", cascade = CascadeType.ALL)
private List<Employee> employees;
Project() {
}
public List<Employee> getEmployees() {
return employess;
}
}
@Entity
public class Employee {
@Id
@Column(name="EMPLOYER_ID", nullable = false)
private long id;
@ManyToOne
@JoinColumn(name="PROJECT_ID")
private Project project;
Emploee() {
}
public Project getProject() {
return project;
}
}
Lets say that the "projectId" in the code below maps to an existing Project,
but one for which no employees exists.
(The call to JPAManager.getInstance() returns a valid EntityManagerFactory (analogous to the commonly used "HibernateUtil" pattern used when running Hibernate without Java EE services))
We have the following code:
Code:
EntityManager em = JPAManager.getInstance().getEntityManager();
try {
em.getTransaction().begin();
Project project = em.find(Project.class, projectId);
// project is here a project for which no employees are defined.
project.getEmployees(); // returns null - not an empty list
em.getTransaction().commit();
} finally {
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
}
As commented - the project.getEmployees() getter here returns 'null' - not an empty list -
is this what one would expect? One actually has to manually handle the "special case" where a relationship has no elements? (what one really would want is an empty list here). If yes, in order to make up for this (which can be a small nuicance when testing) is to initialize the list of employees for a project to an empty list like so:
Code:
@OneToMany(mappedBy="project", cascade = CascadeType.ALL)
private List<Employee> employees = ArrayList<Employee>();
However, when doing this, one stumbles into a problem
when using hibernate as a persistence provider (and perhaps using others as well?)
If a persisted Employee instance is deleted using the entitymanager remove()
and cosecutively persisted using the same entity manager, one receieves the infamous and badly documented:
Code:
Caused by: org.hibernate.HibernateException: Found two representations of same collection: Project.employees
at org.hibernate.engine.Collections.processReachableCollection(Collections.java:176)
at org.hibernate.event.def.FlushVisitor.processCollection(FlushVisitor.java:60)
at org.hibernate.event.def.AbstractVisitor.processValue(AbstractVisitor.java:124)
...
For some reason, the
collection is stuck somewhere in the hibernate session (a call to contains() on the entity manager correctly verifies that the entity indeed is gone from the manager), and no flush() or clear() on the entitymanager can remedy this. Not even getting a handle to the Hibernate session itself and clearing it directly helps (via the getDelegate() method on the entity manager).
The only thing which helps is to create another entity manager.Has anyone any neat solutions to these troubles? I would really like to create my entities with as few JPA-required tweaks as possible (like
not initializing the employees list with an empty list - just to avoid the hibernate exception explained). Moreover, Hibernate is really the desired provider for me as it does not require any JVM agent to enable full lazy loading of mapped entities like for example is required by eclipselink.
I would really appreciate your thoughts on these issues! :)