I have a unidirectional 1:M Parent-Child relationship mapped as entities with "all,delete-orphan" set as the cascade style. I am using Spring's HibernateTemplate for testing. I have a unit test that simulates sending a Parent with a set of Child objects to a Flex client and removes one of the children from the set prior to sending it back to the service layer. The problem I'm seeing is that using hibernateTemplate.saveOrUpdate(parent) doesn't actually remove the child entity but instead marks the child entity's foreign key to the parent as null. If I use merge() instead, the behavior is as expected in that the child entity is deleted as well.
I'd like to map this as a component, but the child properties can be null.
Code:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.tim" default-access="field" >
<class name="Parent"
table="PARENT"
>
<id name="id" type="java.lang.Long">
<column name="PARENT_ID" precision="10" scale="0" />
<generator class="native">
</generator>
</id>
<property name="name" type="java.lang.String">
<column name="NAME" length="255" not-null="true" />
</property>
<set name="children"
cascade="all,delete-orphan">
<key column="PARENT_ID" />
<one-to-many class="Child" />
</set>
</class>
<class name="Child"
table="CHILD">
<id name="id" type="java.lang.Long">
<column name="CHILD_ID" precision="10" scale="0" />
<generator class="native">
</generator>
</id>
<property name="name" type="string">
<column name="CHILD_NAME" length="255" not-null="true" />
</property>
</class>
</hibernate-mapping>
Test case:
public void onSetUp() throws Exception
{
// clean the data
jdbcTemplate.execute( "delete from CHILD" );
jdbcTemplate.execute( "delete from PARENT" );
// stage the data
jdbcTemplate
.execute( "insert into PARENT (PARENT_ID,NAME) values "
+ "(500,'parent1')" );
jdbcTemplate
.execute( "insert into PARENT (PARENT_ID,NAME) values "
+ "(510,'parent2')" );
jdbcTemplate.execute( "insert into CHILD (CHILD_ID, PARENT_ID, CHILD_NAME) values "
+ "(300,500,'child1')" );
jdbcTemplate.execute( "insert into CHILD (CHILD_ID, PARENT_ID, CHILD_NAME) values "
+ "(310,500,'child2')" );
jdbcTemplate.execute( "insert into CHILD (CHILD_ID, PARENT_ID, CHILD_NAME) values "
+ "(320,510,'child3')" );
setComplete();
startNewTransaction();
}
@NotTransactional
public void testRemoveChildFromDetached() throws Exception
{
final Parent parent = new Parent( );
parent.setId( Long.valueOf( "500" ) );
parent.setName( "parent1_modified" );
final Child child1 = new Child();
child1.setName( "child1_modified" );
child1.setId( Long.valueOf( "310") );
final Set<Child> children = new LinkedHashSet<Child>();
children.add( child1 );
parent.setChildren( children );
System.out.println( "******* saveOrUpdate() of parent: " + parent + " *******" );
// NOTE: uncomment the following for the test to pass
// hibernateTemplate.merge( parent );
// NOTE: this line will not result in the child deletion
hibernateTemplate.saveOrUpdate( parent );
endTransaction();
startNewTransaction();
System.out.println( "******* loading parent again *********" );
final Parent parentCheck = (Parent)hibernateTemplate.get( Parent.class, Long.valueOf( 500 ) );
assertTrue( parentCheck.getChildren().size() == 1);
final long rowCount =
jdbcTemplate.queryForLong( "select count(*) from CHILD where CHILD_ID='300'" );
assertEquals( "Count of rows in CHILD should be 0 for CHILD_ID='300'", 0, rowCount );
}
If I execute this test with saveOrUpdate() uncommented, I'll end up with all three child rows (300, 310, 320), but 310 will have a PARENT_ID of null. If I use merge() instead, 310 is deleted and the test passes. Is there a good explanation for this?
Thanks for the help.