I am having a problem with persisting changes to a many-to-many collection in a detached entity with Hibernate Jpa.
I have the basic User and Group objects in a many-to-many relationship via a join table. In the first transaction/session I load a list of users and a list of groups.
Then outside of transaction the UI modifies the user and adds/removes groups, after which it calls the service. In the second transaction the service saves the user, and I expect the join table to be correctly updated to reflect the changes to the groups collection.
This works for newly created user objects. It does NOT work for entities there were previously loaded and became detached. I've tried all kinds of combinations of using cascades (doesn't work and in any case I don't want to cascade operations to groups from users), calling merge() explicitly on groups and clearing and recreating the collection. No matter what I do I can't get Hibernate to insert records into the join table.
I've read a bug report in JIRA which describes exactly this situation in Hibernate 3.2 but it says it's fixed. Am I missing something in configuration, or is the bug still there?
Hibernate version: 3.3.1.GA, Annotations/EntityManager: 3.4.0.GA
Mapping documents:
Code:
@Entity
@Table(name = "AC_USER", schema = "SURV_OWNER")
public class User extends AbstractPersistentEntity {
...
@ManyToMany
@JoinTable(
name = "SURV_OWNER.AC_USER_GROUP",
joinColumns = @JoinColumn(name = "USER_ID"),
inverseJoinColumns = @JoinColumn(name = "GROUP_ID"))
public List<Group> getGroups() {
return groups;
}
}
@Entity
@Table(name = "AC_GROUP", schema = "SURV_OWNER")
public class Group extends AbstractPersistentEntity {
....
@ManyToMany(mappedBy="groups")
public List<User> getUsers() {
return users;
}
}
Code between sessionFactory.openSession() and session.close():Note, this is the latest bastardized version of the service method. Initially I just had a call to entityManager.merge(user) for the case where getId() != null
Code:
public void persistUser(User user) {
logger.debug("Persisting user " + user.getUserName() + ", groups.size = " + user.getGroups().size());
if (user.getId() != null) {
List<Group> groups = new ArrayList<Group>(user.getGroups());
user.getGroups().clear();
logger.debug("Groups before merge = " + user.getGroups());
this.entityManager.merge(user);
logger.debug("Groups after initialize = " + user.getGroups());
for(Group group: groups) {
this.entityManager.merge(group);
user.getGroups().add(group);
}
} else {
entityManager.persist(user);
}
}
Full stack trace of any exception that occurs:
No exceptions
Name and version of the database you are using:
Oracle 10g
The generated SQL (show_sql=true):
Debug level Hibernate log excerpt:
DEBUG [http-8888-Processor4] org.finra.mrw.server.service.accesscontrol.AccessControlServiceImpl 15:43:31 - Persisting user t, groups.size = 2
DEBUG [http-8888-Processor4] org.finra.mrw.server.service.accesscontrol.AccessControlServiceImpl 15:43:31 - Groups before merge = []
DEBUG [http-8888-Processor4] org.springframework.transaction.support.TransactionSynchronizationManager 15:43:31 - Retrieved value [org.springframework.orm.jpa.EntityManagerHolder@d9ecbc] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@a5e899] bound to thread [http-8888-Processor4]
DEBUG [http-8888-Processor4] org.hibernate.loader.Loader 15:43:31 - loading entity: [org.finra.mrw.client.model.accesscontrol.User#411]
DEBUG [http-8888-Processor4] org.hibernate.jdbc.AbstractBatcher 15:43:31 - about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
DEBUG [http-8888-Processor4] org.hibernate.SQL 15:43:31 -
select
user0_.USER_ID as USER1_4_0_,
user0_.FIRST_NM as FIRST2_4_0_,
user0_.LAST_NM as LAST3_4_0_,
user0_.USER_NM as USER4_4_0_
from
SURV_OWNER.AC_USER user0_
where
user0_.USER_ID=?
Hibernate:
select
user0_.USER_ID as USER1_4_0_,
user0_.FIRST_NM as FIRST2_4_0_,
user0_.LAST_NM as LAST3_4_0_,
user0_.USER_NM as USER4_4_0_
from
SURV_OWNER.AC_USER user0_
where
user0_.USER_ID=?
DEBUG [http-8888-Processor4] org.hibernate.jdbc.AbstractBatcher 15:43:31 - about to open ResultSet (open ResultSets: 0, globally: 0)
DEBUG [http-8888-Processor4] org.hibernate.loader.Loader 15:43:31 - result row: EntityKey[org.finra.mrw.client.model.accesscontrol.User#411]
DEBUG [http-8888-Processor4] org.hibernate.jdbc.AbstractBatcher 15:43:31 - about to close ResultSet (open ResultSets: 1, globally: 1)
DEBUG [http-8888-Processor4] org.hibernate.jdbc.AbstractBatcher 15:43:31 - about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
DEBUG [http-8888-Processor4] org.hibernate.engine.TwoPhaseLoad 15:43:31 - resolving associations for [org.finra.mrw.client.model.accesscontrol.User#411]
DEBUG [http-8888-Processor4] org.hibernate.engine.TwoPhaseLoad 15:43:31 - done materializing entity [org.finra.mrw.client.model.accesscontrol.User#411]
DEBUG [http-8888-Processor4] org.hibernate.engine.StatefulPersistenceContext 15:43:31 - initializing non-lazy collections
DEBUG [http-8888-Processor4] org.hibernate.loader.Loader 15:43:31 - done entity load
DEBUG [http-8888-Processor4] org.hibernate.loader.Loader 15:43:31 - loading collection: [org.finra.mrw.client.model.accesscontrol.User.groups#411]
DEBUG [http-8888-Processor4] org.hibernate.jdbc.AbstractBatcher 15:43:31 - about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
DEBUG [http-8888-Processor4] org.hibernate.SQL 15:43:31 -
select
groups0_.USER_ID as USER1_1_,
groups0_.GROUP_ID as GROUP2_1_,
group1_.GROUP_ID as GROUP1_16_0_,
group1_.GROUP_NM as GROUP2_16_0_
from
SURV_OWNER.AC_USER_GROUP groups0_,
SURV_OWNER.AC_GROUP group1_
where
groups0_.GROUP_ID=group1_.GROUP_ID(+)
and groups0_.USER_ID=?
Hibernate:
select
groups0_.USER_ID as USER1_1_,
groups0_.GROUP_ID as GROUP2_1_,
group1_.GROUP_ID as GROUP1_16_0_,
group1_.GROUP_NM as GROUP2_16_0_
from
SURV_OWNER.AC_USER_GROUP groups0_,
SURV_OWNER.AC_GROUP group1_
where
groups0_.GROUP_ID=group1_.GROUP_ID(+)
and groups0_.USER_ID=?
DEBUG [http-8888-Processor4] org.hibernate.jdbc.AbstractBatcher 15:43:31 - about to open ResultSet (open ResultSets: 0, globally: 0)
DEBUG [http-8888-Processor4] org.hibernate.loader.Loader 15:43:31 - result set contains (possibly empty) collection: [org.finra.mrw.client.model.accesscontrol.User.groups#411]
DEBUG [http-8888-Processor4] org.hibernate.jdbc.AbstractBatcher 15:43:31 - about to close ResultSet (open ResultSets: 1, globally: 1)
DEBUG [http-8888-Processor4] org.hibernate.jdbc.AbstractBatcher 15:43:31 - about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
DEBUG [http-8888-Processor4] org.hibernate.engine.loading.CollectionLoadContext 15:43:31 - 1 collections were found in result set for role: org.finra.mrw.client.model.accesscontrol.User.groups
DEBUG [http-8888-Processor4] org.hibernate.engine.loading.CollectionLoadContext 15:43:31 - collection fully initialized: [org.finra.mrw.client.model.accesscontrol.User.groups#411]
DEBUG [http-8888-Processor4] org.hibernate.engine.loading.CollectionLoadContext 15:43:31 - 1 collections initialized for role: org.finra.mrw.client.model.accesscontrol.User.groups
DEBUG [http-8888-Processor4] org.hibernate.engine.StatefulPersistenceContext 15:43:31 - initializing non-lazy collections
DEBUG [http-8888-Processor4] org.hibernate.loader.Loader 15:43:31 - done loading collection
DEBUG [http-8888-Processor4] org.finra.mrw.server.service.accesscontrol.AccessControlServiceImpl 15:43:31 - Groups after initialize = []
DEBUG [http-8888-Processor4] org.springframework.transaction.support.TransactionSynchronizationManager 15:43:31 - Retrieved value [org.springframework.orm.jpa.EntityManagerHolder@d9ecbc] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@a5e899] bound to thread [http-8888-Processor4]
DEBUG [http-8888-Processor4] org.hibernate.loader.Loader 15:43:31 - loading entity: [org.finra.mrw.client.model.accesscontrol.Group#1]
DEBUG [http-8888-Processor4] org.hibernate.jdbc.AbstractBatcher 15:43:31 - about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
DEBUG [http-8888-Processor4] org.hibernate.SQL 15:43:31 -
select
group0_.GROUP_ID as GROUP1_16_0_,
group0_.GROUP_NM as GROUP2_16_0_
from
SURV_OWNER.AC_GROUP group0_
where
group0_.GROUP_ID=?
Hibernate:
select
group0_.GROUP_ID as GROUP1_16_0_,
group0_.GROUP_NM as GROUP2_16_0_
from
SURV_OWNER.AC_GROUP group0_
where
group0_.GROUP_ID=?
DEBUG [http-8888-Processor4] org.hibernate.jdbc.AbstractBatcher 15:43:31 - about to open ResultSet (open ResultSets: 0, globally: 0)
DEBUG [http-8888-Processor4] org.hibernate.loader.Loader 15:43:31 - result row: EntityKey[org.finra.mrw.client.model.accesscontrol.Group#1]
DEBUG [http-8888-Processor4] org.hibernate.jdbc.AbstractBatcher 15:43:31 - about to close ResultSet (open ResultSets: 1, globally: 1)
DEBUG [http-8888-Processor4] org.hibernate.jdbc.AbstractBatcher 15:43:31 - about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
DEBUG [http-8888-Processor4] org.hibernate.engine.TwoPhaseLoad 15:43:31 - resolving associations for [org.finra.mrw.client.model.accesscontrol.Group#1]
DEBUG [http-8888-Processor4] org.hibernate.engine.TwoPhaseLoad 15:43:31 - done materializing entity [org.finra.mrw.client.model.accesscontrol.Group#1]
DEBUG [http-8888-Processor4] org.hibernate.engine.StatefulPersistenceContext 15:43:31 - initializing non-lazy collections
DEBUG [http-8888-Processor4] org.hibernate.loader.Loader 15:43:31 - done entity load
DEBUG [http-8888-Processor4] org.springframework.transaction.support.TransactionSynchronizationManager 15:43:31 - Retrieved value [org.springframework.orm.jpa.EntityManagerHolder@d9ecbc] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@a5e899] bound to thread [http-8888-Processor4]
DEBUG [http-8888-Processor4] org.hibernate.loader.Loader 15:43:31 - loading entity: [org.finra.mrw.client.model.accesscontrol.Group#2]
DEBUG [http-8888-Processor4] org.hibernate.jdbc.AbstractBatcher 15:43:31 - about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
DEBUG [http-8888-Processor4] org.hibernate.SQL 15:43:31 -
select
group0_.GROUP_ID as GROUP1_16_0_,
group0_.GROUP_NM as GROUP2_16_0_
from
SURV_OWNER.AC_GROUP group0_
where
group0_.GROUP_ID=?
Hibernate:
select
group0_.GROUP_ID as GROUP1_16_0_,
group0_.GROUP_NM as GROUP2_16_0_
from
SURV_OWNER.AC_GROUP group0_
where
group0_.GROUP_ID=?
DEBUG [http-8888-Processor4] org.hibernate.jdbc.AbstractBatcher 15:43:31 - about to open ResultSet (open ResultSets: 0, globally: 0)
DEBUG [http-8888-Processor4] org.hibernate.loader.Loader 15:43:31 - result row: EntityKey[org.finra.mrw.client.model.accesscontrol.Group#2]
DEBUG [http-8888-Processor4] org.hibernate.jdbc.AbstractBatcher 15:43:31 - about to close ResultSet (open ResultSets: 1, globally: 1)
DEBUG [http-8888-Processor4] org.hibernate.jdbc.AbstractBatcher 15:43:31 - about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
DEBUG [http-8888-Processor4] org.hibernate.engine.TwoPhaseLoad 15:43:31 - resolving associations for [org.finra.mrw.client.model.accesscontrol.Group#2]
DEBUG [http-8888-Processor4] org.hibernate.engine.TwoPhaseLoad 15:43:31 - done materializing entity [org.finra.mrw.client.model.accesscontrol.Group#2]
DEBUG [http-8888-Processor4] org.hibernate.engine.StatefulPersistenceContext 15:43:31 - initializing non-lazy collections
DEBUG [http-8888-Processor4] org.hibernate.loader.Loader 15:43:31 - done entity load
DEBUG [http-8888-Processor4] org.springframework.transaction.interceptor.TransactionInterceptor 15:43:31 - Completing transaction for [org.finra.mrw.client.service.accesscontrol.AccessControlService.persistUser]
DEBUG [http-8888-Processor4] org.springframework.orm.jpa.JpaTransactionManager 15:43:31 - Triggering beforeCommit synchronization
DEBUG [http-8888-Processor4] org.springframework.orm.jpa.JpaTransactionManager 15:43:31 - Triggering beforeCompletion synchronization
DEBUG [http-8888-Processor4] org.springframework.orm.jpa.JpaTransactionManager 15:43:31 - Initiating transaction commit
DEBUG [http-8888-Processor4] org.springframework.orm.jpa.JpaTransactionManager 15:43:31 - Committing JPA transaction on EntityManager [org.hibernate.ejb.EntityManagerImpl@1e7e2c8]
DEBUG [http-8888-Processor4] org.hibernate.transaction.JDBCTransaction 15:43:31 - commit
DEBUG [http-8888-Processor4] org.hibernate.event.def.AbstractFlushingEventListener 15:43:31 - processing flush-time cascades
DEBUG [http-8888-Processor4] org.hibernate.event.def.AbstractFlushingEventListener 15:43:31 - dirty checking collections
DEBUG [http-8888-Processor4] org.hibernate.engine.Collections 15:43:31 - Collection found: [org.finra.mrw.client.model.accesscontrol.User.groups#411], was: [org.finra.mrw.client.model.accesscontrol.User.groups#411] (initialized)
DEBUG [http-8888-Processor4] org.hibernate.engine.Collections 15:43:31 - Collection found: [org.finra.mrw.client.model.accesscontrol.Group.roles#1], was: [org.finra.mrw.client.model.accesscontrol.Group.roles#1] (uninitialized)
DEBUG [http-8888-Processor4] org.hibernate.engine.Collections 15:43:31 - Collection found: [org.finra.mrw.client.model.accesscontrol.Group.users#1], was: [org.finra.mrw.client.model.accesscontrol.Group.users#1] (uninitialized)
DEBUG [http-8888-Processor4] org.hibernate.engine.Collections 15:43:31 - Collection found: [org.finra.mrw.client.model.accesscontrol.Group.roles#2], was: [org.finra.mrw.client.model.accesscontrol.Group.roles#2] (uninitialized)
DEBUG [http-8888-Processor4] org.hibernate.engine.Collections 15:43:31 - Collection found: [org.finra.mrw.client.model.accesscontrol.Group.users#2], was: [org.finra.mrw.client.model.accesscontrol.Group.users#2] (uninitialized)
DEBUG [http-8888-Processor4] org.hibernate.event.def.AbstractFlushingEventListener 15:43:31 - Flushed: 0 insertions, 0 updates, 0 deletions to 3 objects
DEBUG [http-8888-Processor4] org.hibernate.event.def.AbstractFlushingEventListener 15:43:31 - Flushed: 0 (re)creations, 0 updates, 0 removals to 5 collections
DEBUG [http-8888-Processor4] org.hibernate.pretty.Printer 15:43:31 - listing entities:
DEBUG [http-8888-Processor4] org.hibernate.pretty.Printer 15:43:31 - org.finra.mrw.client.model.accesscontrol.User{userName=t, groups=[], firstName=t4, id=411, lastName=t}
DEBUG [http-8888-Processor4] org.hibernate.pretty.Printer 15:43:31 - org.finra.mrw.client.model.accesscontrol.Group{users=<uninitialized>, roles=<uninitialized>, name=Users, id=1}
DEBUG [http-8888-Processor4] org.hibernate.pretty.Printer 15:43:31 - org.finra.mrw.client.model.accesscontrol.Group{users=<uninitialized>, roles=<uninitialized>, name=Administrators, id=2}
DEBUG [http-8888-Processor4] org.hibernate.transaction.JDBCTransaction 15:43:32 - re-enabling autocommit
DEBUG [http-8888-Processor4] org.hibernate.transaction.JDBCTransaction 15:43:32 - committed JDBC Connection