I'm updating a project that was using Hibernate version 3.0.5 to use Hibernate version 3.3.1
In the model we have an entity that contains a component.
The component has a collection of strings.
In Hibernate 3.0.5 when we used session.merge(...), this collection of strings is persisted.
But using Hibernate 3.3.1 session.merge(...) does
not persist the collection of strings.
If I use session.save(...), then the collection of strings is persisted (in both versions).
I really don't get why 3.3.1 is not merging the collection when it merges the component.
Does anyone have any clues as to why the behaviour has changed?
Here's the simplified model that illustrates this:
Code:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping auto-import="true" default-lazy="false">
<class name="com.mockmodel.MockUser" table="TST_MOCK_USER">
<id name="id" type="string" unsaved-value="null" column="ID">
<generator class="uuid"/>
</id>
<property name="name" not-null="true" unique="true" column="NAME"/>
<component name="statusInfo" class="com.mockmodel.MockStatusInfo">
<property name="currentStatus" column="CURRENT_STATUS" not-null="true"/>
<list name="previousStatusList" table="TST_PREVIOUS_STATUS">
<key column="MOCK_USER_ID" foreign-key="FK_PREV_STATUS_MOCK_USER_ID"/>
<list-index column="INDEX_ORDER"/>
<element column="PREVIOUS_STATUS" not-null="true" type="string"/>
</list>
</component>
</class>
</hibernate-mapping>
MockUser has a component: MockStatusInfo.
In MockStatusInfo we have a list of strings: previousStatusList
In
Hibernate 3.0.5 when I create a new MockUser and merge it to the session, the strings in previousStatusList on the MockStatusInfo also get stored in the database.
However, in
Hibernate 3.3.1 the previousStatusList in the MockStatusInfo is not stored.
Since previousStatusList is part of MockStatusInfo and MockStatusInfo is a component of MockUser I had expected the merge to continue to work.
Does anyone have an explanation as to what has changed?
For further information here is some sample code: you'll need to edit MockModelMain to give it a working configuration.
com.mockmodel.MockUser:
Code:
package com.mockmodel;
public class MockUser
{
private String id;
private String name;
private MockStatusInfo statusInfo;
public MockUser(String name, String currentStatus)
{
this.name = name;
statusInfo = new MockStatusInfo(currentStatus);
}
public String getId()
{
return id;
}
public void setId(String id)
{
this.id = id;
}
public MockStatusInfo getStatusInfo()
{
return statusInfo;
}
public void setStatusInfo(MockStatusInfo statusInfo)
{
this.statusInfo = statusInfo;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
private MockUser()
{
}
}
com.mockmodel.MockStatusInfo:
Code:
package com.mockmodel;
import java.util.*;
public class MockStatusInfo
{
private String currentStatus;
private List<String> previousStatusList;
public MockStatusInfo(String currentStatus)
{
this.currentStatus = currentStatus;
previousStatusList = new ArrayList<String>();
}
private MockStatusInfo()
{
}
public List<String> getPreviousStatusList()
{
return previousStatusList;
}
public void setPreviousStatusList(List<String> previousStatusList)
{
this.previousStatusList = previousStatusList;
}
public String getCurrentStatus()
{
return currentStatus;
}
public void setCurrentStatus(String currentStatus)
{
this.currentStatus = currentStatus;
}
}
com.mockmodel.MockUserService:
Code:
package com.mockmodel;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.classic.Session;
public class MockUserService
{
private final SessionFactory sessionFactory;
public MockUserService(SessionFactory sessionFactory)
{
this.sessionFactory = sessionFactory;
}
private void mergeUserInTransaction(MockUser mockUser)
{
Session session = sessionFactory.openSession();
try
{
Transaction transaction = session.beginTransaction();
session.merge(mockUser);
transaction.commit();
}
finally
{
session.close();
}
}
private void saveUserInTransaction(MockUser mockUser)
{
Session session = sessionFactory.openSession();
try
{
Transaction transaction = session.beginTransaction();
session.save(mockUser);
transaction.commit();
}
finally
{
session.close();
}
}
private static MockUser createUser(String name)
{
MockUser mockUser = new MockUser(name, "first status");
mockUser.getStatusInfo().getPreviousStatusList().add("previous status");
return mockUser;
}
public void mergeUser(String name)
{
System.out.println("USER-SERVICE:start merge user:" + name);
mergeUserInTransaction(createUser(name));
System.out.println("USER-SERVICE:end merge user:" + name);
}
public void saveUser(String name)
{
System.out.println("USER-SERVICE:start save user:" + name);
saveUserInTransaction(createUser(name));
System.out.println("USER-SERVICE:end save user:" + name);
}
}
com.mockmodel.MockModelMain:
Code:
package com.mockmodel;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class MockModelMain
{
public static MockUserService createMockUserService()
{
Configuration cfg = new Configuration();
cfg.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLInnoDBDialect");
cfg.setProperty("hibernate.connection.driver_class", "com.mysql.jdbc.Driver");
cfg.setProperty("hibernate.connection.username", "[FIXME-db-username]");
cfg.setProperty("hibernate.connection.password", "[FIXME-db-password]");
cfg.setProperty("hibernate.connection.url", "jdbc:mysql://[FIXME-mysql-host]:3306/dev_fozzie");
cfg.setProperty("hibernate.hbm2ddl.auto", "create");
cfg.setProperty("hibernate.show_sql", "true");
cfg.addResource("com/mockmodel/mockmodel.hbm.xml");
SessionFactory sessionFactory = cfg.buildSessionFactory();
return new MockUserService(sessionFactory);
}
public static void main(String[] args)
{
MockUserService mockUserService = createMockUserService();
mockUserService.mergeUser("user-merged");
mockUserService.saveUser("user-saved");
}
}
I enabled logging for hibernate at debug level, directed to standard out.
Here's an excerpt of the output when run using Hibernate 3.3.1:
Code:
USER-SERVICE:start merge user:user-merged
2009-04-09 15:01:39,573 DEBUG [org.hibernate.impl.SessionImpl] - <opened session at timestamp: 12392856994>
2009-04-09 15:01:39,573 DEBUG [org.hibernate.transaction.JDBCTransaction] - <begin>
2009-04-09 15:01:39,573 DEBUG [org.hibernate.jdbc.ConnectionManager] - <opening JDBC connection>
2009-04-09 15:01:39,573 DEBUG [org.hibernate.transaction.JDBCTransaction] - <current autocommit status: false>
2009-04-09 15:01:39,604 DEBUG [org.hibernate.event.def.AbstractSaveEventListener] - <generated identifier: 4028820a208b2f1101208b2f14140001, using strategy: org.hibernate.id.UUIDHexGenerator>
2009-04-09 15:01:39,636 DEBUG [org.hibernate.transaction.JDBCTransaction] - <commit>
2009-04-09 15:01:39,636 DEBUG [org.hibernate.event.def.AbstractFlushingEventListener] - <processing flush-time cascades>
2009-04-09 15:01:39,636 DEBUG [org.hibernate.event.def.AbstractFlushingEventListener] - <dirty checking collections>
2009-04-09 15:01:39,636 DEBUG [org.hibernate.event.def.AbstractFlushingEventListener] - <Flushed: 1 insertions, 0 updates, 0 deletions to 1 objects>
2009-04-09 15:01:39,636 DEBUG [org.hibernate.event.def.AbstractFlushingEventListener] - <Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections>
2009-04-09 15:01:39,636 DEBUG [org.hibernate.pretty.Printer] - <listing entities:>
2009-04-09 15:01:39,636 DEBUG [org.hibernate.pretty.Printer] - <com.mockmodel.MockUser{statusInfo=component[currentStatus,previousStatusList]{currentStatus=first status, previousStatusList=null}, name=user-merged, id=4028820a208b2f1101208b2f14140001}>
2009-04-09 15:01:39,651 DEBUG [org.hibernate.jdbc.AbstractBatcher] - <about to open PreparedStatement (open PreparedStatements: 0, globally: 0)>
2009-04-09 15:01:39,651 DEBUG [org.hibernate.SQL] - <insert into TST_MOCK_USER (NAME, CURRENT_STATUS, ID) values (?, ?, ?)>
Hibernate: insert into TST_MOCK_USER (NAME, CURRENT_STATUS, ID) values (?, ?, ?)
2009-04-09 15:01:39,667 DEBUG [org.hibernate.jdbc.AbstractBatcher] - <Executing batch size: 1>
2009-04-09 15:01:39,667 DEBUG [org.hibernate.jdbc.AbstractBatcher] - <about to close PreparedStatement (open PreparedStatements: 1, globally: 1)>
2009-04-09 15:01:39,683 DEBUG [org.hibernate.transaction.JDBCTransaction] - <committed JDBC Connection>
2009-04-09 15:01:39,683 DEBUG [org.hibernate.jdbc.ConnectionManager] - <aggressively releasing JDBC connection>
2009-04-09 15:01:39,683 DEBUG [org.hibernate.jdbc.ConnectionManager] - <releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)]>
USER-SERVICE:end merge user:user-merged
USER-SERVICE:start save user:user-saved
2009-04-09 15:01:39,683 DEBUG [org.hibernate.impl.SessionImpl] - <opened session at timestamp: 12392856996>
2009-04-09 15:01:39,683 DEBUG [org.hibernate.transaction.JDBCTransaction] - <begin>
2009-04-09 15:01:39,683 DEBUG [org.hibernate.jdbc.ConnectionManager] - <opening JDBC connection>
2009-04-09 15:01:39,683 DEBUG [org.hibernate.transaction.JDBCTransaction] - <current autocommit status: false>
2009-04-09 15:01:39,683 DEBUG [org.hibernate.event.def.AbstractSaveEventListener] - <generated identifier: 4028820a208b2f1101208b2f14630002, using strategy: org.hibernate.id.UUIDHexGenerator>
2009-04-09 15:01:39,698 DEBUG [org.hibernate.transaction.JDBCTransaction] - <commit>
2009-04-09 15:01:39,698 DEBUG [org.hibernate.event.def.AbstractFlushingEventListener] - <processing flush-time cascades>
2009-04-09 15:01:39,698 DEBUG [org.hibernate.event.def.AbstractFlushingEventListener] - <dirty checking collections>
2009-04-09 15:01:39,698 DEBUG [org.hibernate.engine.Collections] - <Collection found: [com.mockmodel.MockUser.statusInfo.previousStatusList#4028820a208b2f1101208b2f14630002], was: [<unreferenced>] (initialized)>
2009-04-09 15:01:39,698 DEBUG [org.hibernate.event.def.AbstractFlushingEventListener] - <Flushed: 1 insertions, 0 updates, 0 deletions to 1 objects>
2009-04-09 15:01:39,698 DEBUG [org.hibernate.event.def.AbstractFlushingEventListener] - <Flushed: 1 (re)creations, 0 updates, 0 removals to 1 collections>
2009-04-09 15:01:39,698 DEBUG [org.hibernate.pretty.Printer] - <listing entities:>
2009-04-09 15:01:39,698 DEBUG [org.hibernate.pretty.Printer] - <com.mockmodel.MockUser{statusInfo=component[currentStatus,previousStatusList]{currentStatus=first status, previousStatusList=[previous status]}, name=user-saved, id=4028820a208b2f1101208b2f14630002}>
2009-04-09 15:01:39,698 DEBUG [org.hibernate.jdbc.AbstractBatcher] - <about to open PreparedStatement (open PreparedStatements: 0, globally: 0)>
2009-04-09 15:01:39,698 DEBUG [org.hibernate.SQL] - <insert into TST_MOCK_USER (NAME, CURRENT_STATUS, ID) values (?, ?, ?)>
Hibernate: insert into TST_MOCK_USER (NAME, CURRENT_STATUS, ID) values (?, ?, ?)
2009-04-09 15:01:39,698 DEBUG [org.hibernate.jdbc.AbstractBatcher] - <Executing batch size: 1>
2009-04-09 15:01:39,698 DEBUG [org.hibernate.jdbc.AbstractBatcher] - <about to close PreparedStatement (open PreparedStatements: 1, globally: 1)>
2009-04-09 15:01:39,698 DEBUG [org.hibernate.persister.collection.AbstractCollectionPersister] - <Inserting collection: [com.mockmodel.MockUser.statusInfo.previousStatusList#4028820a208b2f1101208b2f14630002]>
2009-04-09 15:01:39,698 DEBUG [org.hibernate.jdbc.AbstractBatcher] - <about to open PreparedStatement (open PreparedStatements: 0, globally: 0)>
2009-04-09 15:01:39,698 DEBUG [org.hibernate.SQL] - <insert into TST_PREVIOUS_STATUS (MOCK_USER_ID, INDEX_ORDER, PREVIOUS_STATUS) values (?, ?, ?)>
Hibernate: insert into TST_PREVIOUS_STATUS (MOCK_USER_ID, INDEX_ORDER, PREVIOUS_STATUS) values (?, ?, ?)
2009-04-09 15:01:39,698 DEBUG [org.hibernate.persister.collection.AbstractCollectionPersister] - <done inserting collection: 1 rows inserted>
2009-04-09 15:01:39,698 DEBUG [org.hibernate.jdbc.AbstractBatcher] - <Executing batch size: 1>
2009-04-09 15:01:39,698 DEBUG [org.hibernate.jdbc.AbstractBatcher] - <about to close PreparedStatement (open PreparedStatements: 1, globally: 1)>
2009-04-09 15:01:39,714 DEBUG [org.hibernate.transaction.JDBCTransaction] - <committed JDBC Connection>
2009-04-09 15:01:39,714 DEBUG [org.hibernate.jdbc.ConnectionManager] - <aggressively releasing JDBC connection>
2009-04-09 15:01:39,714 DEBUG [org.hibernate.jdbc.ConnectionManager] - <releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)]>
USER-SERVICE:end save user:user-saved
When run using Hibernate 3.0.5:
Code:
USER-SERVICE:start merge user:user-merged
2009-04-09 15:05:06,262 DEBUG [org.hibernate.impl.SessionImpl] - <opened session at timestamp: 5076115071791104>
2009-04-09 15:05:06,262 DEBUG [org.hibernate.transaction.JDBCTransaction] - <begin>
2009-04-09 15:05:06,262 DEBUG [org.hibernate.jdbc.ConnectionManager] - <opening JDBC connection>
2009-04-09 15:05:06,262 DEBUG [org.hibernate.transaction.JDBCTransaction] - <current autocommit status: false>
2009-04-09 15:05:06,278 DEBUG [org.hibernate.event.def.AbstractSaveEventListener] - <generated identifier: 4028820a208b323901208b323b660001, using strategy: org.hibernate.id.UUIDHexGenerator>
2009-04-09 15:05:06,356 DEBUG [org.hibernate.transaction.JDBCTransaction] - <commit>
2009-04-09 15:05:06,356 DEBUG [org.hibernate.event.def.AbstractFlushingEventListener] - <processing flush-time cascades>
2009-04-09 15:05:06,356 DEBUG [org.hibernate.event.def.AbstractFlushingEventListener] - <dirty checking collections>
2009-04-09 15:05:06,356 DEBUG [org.hibernate.engine.Collections] - <Collection found: [com.mockmodel.MockUser.com.mockmodel.MockUser.statusInfo.previousStatusList#4028820a208b323901208b323b660001], was: [<unreferenced>] (initialized)>
2009-04-09 15:05:06,371 DEBUG [org.hibernate.event.def.AbstractFlushingEventListener] - <Flushed: 1 insertions, 0 updates, 0 deletions to 1 objects>
2009-04-09 15:05:06,371 DEBUG [org.hibernate.event.def.AbstractFlushingEventListener] - <Flushed: 1 (re)creations, 0 updates, 0 removals to 1 collections>
2009-04-09 15:05:06,371 DEBUG [org.hibernate.pretty.Printer] - <listing entities:>
2009-04-09 15:05:06,371 DEBUG [org.hibernate.pretty.Printer] - <com.mockmodel.MockUser{statusInfo=component[currentStatus,previousStatusList]{currentStatus=first status, previousStatusList=[previous status]}, name=user-merged, id=4028820a208b323901208b323b660001}>
2009-04-09 15:05:06,371 DEBUG [org.hibernate.jdbc.AbstractBatcher] - <about to open PreparedStatement (open PreparedStatements: 0, globally: 0)>
2009-04-09 15:05:06,371 DEBUG [org.hibernate.SQL] - <insert into TST_MOCK_USER (NAME, CURRENT_STATUS, ID) values (?, ?, ?)>
Hibernate: insert into TST_MOCK_USER (NAME, CURRENT_STATUS, ID) values (?, ?, ?)
2009-04-09 15:05:06,387 DEBUG [org.hibernate.jdbc.AbstractBatcher] - <Executing batch size: 1>
2009-04-09 15:05:06,387 DEBUG [org.hibernate.jdbc.AbstractBatcher] - <about to close PreparedStatement (open PreparedStatements: 1, globally: 1)>
2009-04-09 15:05:06,387 DEBUG [org.hibernate.persister.collection.AbstractCollectionPersister] - <Inserting collection: [com.mockmodel.MockUser.com.mockmodel.MockUser.statusInfo.previousStatusList#4028820a208b323901208b323b660001]>
2009-04-09 15:05:06,387 DEBUG [org.hibernate.jdbc.AbstractBatcher] - <about to open PreparedStatement (open PreparedStatements: 0, globally: 0)>
2009-04-09 15:05:06,387 DEBUG [org.hibernate.SQL] - <insert into TST_PREVIOUS_STATUS (MOCK_USER_ID, INDEX_ORDER, PREVIOUS_STATUS) values (?, ?, ?)>
Hibernate: insert into TST_PREVIOUS_STATUS (MOCK_USER_ID, INDEX_ORDER, PREVIOUS_STATUS) values (?, ?, ?)
2009-04-09 15:05:06,387 DEBUG [org.hibernate.persister.collection.AbstractCollectionPersister] - <done inserting collection: 1 rows inserted>
2009-04-09 15:05:06,387 DEBUG [org.hibernate.jdbc.AbstractBatcher] - <Executing batch size: 1>
2009-04-09 15:05:06,403 DEBUG [org.hibernate.jdbc.AbstractBatcher] - <about to close PreparedStatement (open PreparedStatements: 1, globally: 1)>
2009-04-09 15:05:06,403 DEBUG [org.hibernate.transaction.JDBCTransaction] - <committed JDBC Connection>
2009-04-09 15:05:06,403 DEBUG [org.hibernate.jdbc.ConnectionManager] - <closing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)]>
USER-SERVICE:end merge user:user-merged
USER-SERVICE:start save user:user-saved
2009-04-09 15:05:06,403 DEBUG [org.hibernate.impl.SessionImpl] - <opened session at timestamp: 5076115072626688>
2009-04-09 15:05:06,403 DEBUG [org.hibernate.transaction.JDBCTransaction] - <begin>
2009-04-09 15:05:06,403 DEBUG [org.hibernate.jdbc.ConnectionManager] - <opening JDBC connection>
2009-04-09 15:05:06,403 DEBUG [org.hibernate.transaction.JDBCTransaction] - <current autocommit status: false>
2009-04-09 15:05:06,403 DEBUG [org.hibernate.event.def.AbstractSaveEventListener] - <generated identifier: 4028820a208b323901208b323be30002, using strategy: org.hibernate.id.UUIDHexGenerator>
2009-04-09 15:05:06,403 DEBUG [org.hibernate.transaction.JDBCTransaction] - <commit>
2009-04-09 15:05:06,403 DEBUG [org.hibernate.event.def.AbstractFlushingEventListener] - <processing flush-time cascades>
2009-04-09 15:05:06,403 DEBUG [org.hibernate.event.def.AbstractFlushingEventListener] - <dirty checking collections>
2009-04-09 15:05:06,403 DEBUG [org.hibernate.engine.Collections] - <Collection found: [com.mockmodel.MockUser.com.mockmodel.MockUser.statusInfo.previousStatusList#4028820a208b323901208b323be30002], was: [<unreferenced>] (initialized)>
2009-04-09 15:05:06,403 DEBUG [org.hibernate.event.def.AbstractFlushingEventListener] - <Flushed: 1 insertions, 0 updates, 0 deletions to 1 objects>
2009-04-09 15:05:06,403 DEBUG [org.hibernate.event.def.AbstractFlushingEventListener] - <Flushed: 1 (re)creations, 0 updates, 0 removals to 1 collections>
2009-04-09 15:05:06,403 DEBUG [org.hibernate.pretty.Printer] - <listing entities:>
2009-04-09 15:05:06,403 DEBUG [org.hibernate.pretty.Printer] - <com.mockmodel.MockUser{statusInfo=component[currentStatus,previousStatusList]{currentStatus=first status, previousStatusList=[previous status]}, name=user-saved, id=4028820a208b323901208b323be30002}>
2009-04-09 15:05:06,403 DEBUG [org.hibernate.jdbc.AbstractBatcher] - <about to open PreparedStatement (open PreparedStatements: 0, globally: 0)>
2009-04-09 15:05:06,403 DEBUG [org.hibernate.SQL] - <insert into TST_MOCK_USER (NAME, CURRENT_STATUS, ID) values (?, ?, ?)>
Hibernate: insert into TST_MOCK_USER (NAME, CURRENT_STATUS, ID) values (?, ?, ?)
2009-04-09 15:05:06,403 DEBUG [org.hibernate.jdbc.AbstractBatcher] - <Executing batch size: 1>
2009-04-09 15:05:06,403 DEBUG [org.hibernate.jdbc.AbstractBatcher] - <about to close PreparedStatement (open PreparedStatements: 1, globally: 1)>
2009-04-09 15:05:06,403 DEBUG [org.hibernate.persister.collection.AbstractCollectionPersister] - <Inserting collection: [com.mockmodel.MockUser.com.mockmodel.MockUser.statusInfo.previousStatusList#4028820a208b323901208b323be30002]>
2009-04-09 15:05:06,403 DEBUG [org.hibernate.jdbc.AbstractBatcher] - <about to open PreparedStatement (open PreparedStatements: 0, globally: 0)>
2009-04-09 15:05:06,403 DEBUG [org.hibernate.SQL] - <insert into TST_PREVIOUS_STATUS (MOCK_USER_ID, INDEX_ORDER, PREVIOUS_STATUS) values (?, ?, ?)>
Hibernate: insert into TST_PREVIOUS_STATUS (MOCK_USER_ID, INDEX_ORDER, PREVIOUS_STATUS) values (?, ?, ?)
2009-04-09 15:05:06,403 DEBUG [org.hibernate.persister.collection.AbstractCollectionPersister] - <done inserting collection: 1 rows inserted>
2009-04-09 15:05:06,403 DEBUG [org.hibernate.jdbc.AbstractBatcher] - <Executing batch size: 1>
2009-04-09 15:05:06,403 DEBUG [org.hibernate.jdbc.AbstractBatcher] - <about to close PreparedStatement (open PreparedStatements: 1, globally: 1)>
2009-04-09 15:05:06,418 DEBUG [org.hibernate.transaction.JDBCTransaction] - <committed JDBC Connection>
2009-04-09 15:05:06,418 DEBUG [org.hibernate.jdbc.ConnectionManager] - <closing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)]>
USER-SERVICE:end save user:user-saved