I have two database tables, User
and Role
. I'm trying to set up a bidirectional many-to-many mapping in my application but I'm running into performance issues when eagerly fetching. The many-to-many works using a UserRole
join table. My DTOs look like this (fat has been trimmed):
UserDTO.java
@Entity
@Table(name = "[redacted].User")
public class UserDTO implements Serializable {
/* other field declarations snipped */
private Set<RoleDTO> roles = new HashSet<RoleDTO>();
public UserDTO() {}
/* other getters/setters snipped */
@Fetch(FetchMode.JOIN)
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "[redacted].UserRole",
joinColumns = { @JoinColumn(name = "userId") },
inverseJoinColumns = { @JoinColumn(name = "roleId") })
public Set<RoleDTO> getRoles() {
return roles;
}
public void setRoles(Set<RoleDTO> roles) {
this.roles = roles;
}
}
RoleDTO.java
@Entity
@Table(name = "[redacted].Role")
public class RoleDTO implements Serializable {
/* other field declarations snipped */
private Set<UserDTO> users = new HashSet<UserDTO>();
public RoleDTO() {}
/* other getters/setters snipped */
@Fetch(FetchMode.JOIN)
@ManyToMany(mappedBy = "roles")
public Set<UserDTO> getUsers() {
return users;
}
public void setUsers(Set<UserDTO> users) {
this.users = users;
}
}
All is well with the world, except that things run really slowly - like, O(30 seconds) to update 5 user rows where each user has 2 roles. I think it's because of the eager fetching. I've tried looking at the SQL that Hibernate generates, but I can't tease anything meaningful out of it. I can post a link to a SQL dump if anyone's interested.
At any rate, I'd like to try to fix this by switching to lazy loading. When I change @ManyToMany(fetch = FetchType.EAGER)
to @ManyToMany(fetch = FetchType.LAZY)
, I get the classic error
org.hibernate.LazyInitializationException: failed to lazily initialize a collection [blah blah] no session or session was closed.
I understand why this exception is thrown, but I don't know the best way to fix it.
- Keep the session (transaction?) open for as long as I need to? How?
- Omit the mapping from my EJB entirely, and just manually fetch a user's roles (and a role's user) in my application logic? This feels like a poor man's lazy load, and I have no idea of how I'd handle pushing changes back into the database without explicitly editing the join table (which sounds really unappealing).
- Something else?
If it matters, my beans are @Stateless
, since that's how I've seen it done on other projects.