HibernateException: Illegal Collection Association Fix
In Hibernate, the exception IllegalAttemptToAssociateACollectionWithTwoOpenSessionsException occurs when a collection or entity is associated with two active Hibernate sessions simultaneously. This is a common problem when working with detached entities or reusing objects across sessions. Let us delve into understanding the Hibernate illegal association attempt of a collection to multiple Sessions issue and how to resolve it effectively.
1. Introduction
Hibernate is an Object-Relational Mapping (ORM) framework that helps developers manage database operations using Java objects. One of its core concepts is the session, which represents a unit of work with the database. Each session maintains a first-level cache, tracking changes to entities and collections that it manages. When you attempt to associate a persistent collection or entity with more than one active session, Hibernate is unable to reconcile the two session contexts. This leads to the following runtime exception:
org.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions
This exception commonly occurs in real-world applications under these scenarios:
- Passing entities between multiple sessions without proper detachment: For example, fetching an entity in one session and trying to update it in another without detaching or merging it.
- Using long-running sessions: When a session is kept open for a long time (such as in desktop apps or legacy systems), entities remain attached to the original session. Attempting to use them in a new session can trigger this exception.
- Incorrect transaction boundaries: Transactions define the lifespan of a session’s persistence context. Failing to commit or close sessions at appropriate times can leave collections associated with multiple active sessions.
Understanding how Hibernate sessions, transactions, and entity states (transient, persistent, detached) work is crucial to avoiding this exception.
1.1 Entity States in Hibernate/JPA
Hibernate entities go through four lifecycle states: Transient, Persistent, Detached, and Removed. The table below explains each state with examples and notes.
| State | Description | Code Example | Key Points |
|---|---|---|---|
| Transient | Entity not associated with any persistence context. No DB identity yet. | Department dept = new Department();
dept.setName("Engineering");
// Transient: not saved, not tracked
|
|
| Persistent (Managed) | Entity is associated with an open persistence context. Hibernate tracks changes and flushes automatically. | Department dept = new Department();
dept.setName("Engineering");
entityManager.persist(dept);
dept.setName("Platform");
// INSERT + UPDATE on commit
|
|
| Detached | Entity was persistent but the context is closed or cleared. Still has identity but not tracked anymore. | Department dept = em.find(Department.class, 1L);
em.close();
dept.setName("New Name");
// Detached: not tracked
Department managed = em.merge(dept);
// 'managed' is persistent
|
|
| Removed | Entity is scheduled for deletion from DB. Still managed until transaction commits. | Department dept = em.find(Department.class, 5L); em.remove(dept); // DELETE on commit |
|
2. Code Example
To better understand this problem, let’s consider a simple application with two entities: Employee and Department. The relationship between them is a One-to-Many association: one department can have multiple employees, and each employee belongs to one department. The entities are defined as follows:
// Employee.java
@Entity
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToOne
private Department department;
// getters and setters
}
// Department.java
@Entity
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "department", cascade = CascadeType.ALL)
private List<Employee> employees = new ArrayList();
// getters and setters
}
Here, the Department class owns a list of employees, and each Employee has a reference back to its Department. The cascade = CascadeType.ALL ensures that when we save or delete a department, the related employees are also affected automatically. Now, let’s simulate the problematic scenario where the exception occurs:
Session session1 = sessionFactory.openSession();
session1.beginTransaction();
// Step 1: Load department in the first session
Department dept = session1.get(Department.class, 1L);
session1.getTransaction().commit();
session1.close(); // Now the department is in a DETACHED state
// Step 2: Modify department
dept.setName("New Name");
// Step 3: Try attaching to a new session
Session session2 = sessionFactory.openSession();
session2.beginTransaction();
session2.update(dept);
// Throws IllegalAttemptToAssociateACollectionWithTwoOpenSessionsException
session2.getTransaction().commit();
session2.close();
The exception occurs because dept.getEmployees() collection is still associated with the first session.
2.1 Why does this happen?
- When
deptwas fetched insession1, itsemployeescollection became associated with that session’s persistence context. - After closing
session1, thedeptobject (and its collection) entered the detached state. - Calling
update()ondeptinsession2tries to reattach the same collection to a different session, which Hibernate disallows. - As a result, Hibernate throws
IllegalAttemptToAssociateACollectionWithTwoOpenSessionsException.
3. Solutions
There are several approaches to fix this issue:
| Title | Description | Code Fix | |
|---|---|---|---|
| 1 | Use merge() instead of update() | The merge() method copies the state of the detached object into the persistent context of the new session. | Session session2 = sessionFactory.openSession(); session2.beginTransaction(); session2.merge(dept); // Safe session2.getTransaction().commit(); session2.close(); |
| 2 | Initialize collections before closing the session | Use Hibernate.initialize() or fetch collections eagerly to avoid detached collection issues. | Hibernate.initialize(dept.getEmployees()); session1.getTransaction().commit(); session1.close(); |
| 3 | Use OpenSessionInView pattern (for web apps) | This ensures a single session per request, reducing the chance of “Hibernate Illegal Attempt Collection Sessions” exceptions. | — |
| 4 | Detach entities properly | Detach the entity from the first session before associating it with the next session. | session1.evict(dept); session2.update(dept); |
| 5 | Spring Boot Example with merge() | This approach ensures Hibernate safely merges detached entities with the current persistence context. | @Service
public class DepartmentService {
@Autowired
private EntityManager entityManager;
@Transactional
public Department updateDepartment(Department dept) {
return entityManager.merge(dept);
}
}
|
4. Conclusion
The IllegalAttemptToAssociateACollectionWithTwoOpenSessionsException is a common Hibernate problem when working with detached entities or collections across multiple sessions. Understanding Hibernate session management and using methods like merge() or proper detachment strategies helps prevent this exception. By following best practices—such as maintaining clear session boundaries, initializing collections, and preferring merge()—developers can efficiently manage entity state and avoid runtime exceptions.

