Hi,
I have an issue in my multi-tiered application where I have detached my objects from the Session.
I have a user edit via the GUI the properties of a parent object along with adding, editing, and removal of child objects (collection within the parent).
I am cascading all including orphaned deletes between the parent object and its children.
Upon associating the objects with a Session (saveOrUpdate etc.) the inserts and deletes are occurring but the order in which it occurs violates a unique
constraint
of the business (natural) key of the child object if the user of the GUI deletes and then adds back in the same business key).
After consulting the book "Hibernate in Action" and also the online reference pages
I still could not seem to find a definitive answer to the problem of unique constraints being violated
when an item has been removed and then added back into a collection of children of a parent object.
Gavin has stated, in the posting below..
http://forum.hibernate.org/viewtopic.ph ... e25f5c73c1
that Hibernate never issues the deletes before the inserts.
The documentation states the following ordering on a call to flush()
1. all entity insertions in the same order as the Save was called on them
2. all entity updates
3. all collection deletions
4. all collection element deletions, updates and insertions
5. all collection insertions
6. all entity deletions in the same order as the delete was called.
4. Implies the order as being "deletions, updates and insertions" or is the documentation not
strictly specifying an order here?
Hibernate appears not to match this order as inserts are occurring before deletes for my collection elements.
If Hibernate always issues the inserts first then what is the recommended solution to this problem?
Do we have to step out of the Hibernate world and handle this relationship ourselves?
If so, then fine, but I really do not want to have to code around this if there is a
solution available in Hibernate.
If I have a table with a surrogate primary key for the <id> AND a unique constraint
on another column(s) can I get Hibernate to honour the unique constraint when it calls flush()
by tweaking attributes in the mapping file?
From the online reference documentation:
"Hibernate however doesn't have enough information to correctly arrange SQL INSERT and UPDATE statements (to avoid constraint violations), and needs some
help to handle bi-directional associations properly. Making one side of the association inverse tells Hibernate to basically ignore it, to consider it a
mirror of the other side. That's all that is necessary for Hibernate to work out all of the issues when transformation a directional navigation model to a
SQL database schema."
I have the inverse="true" attribute included.
I have also tried setting the following attributes on the <many-to-one> relationship in the mapping file
Code:
index="index_name"
unique_key="unique_key_id"
foreign-key="foreign_key_name"
although the docs do not go into any detail regarding their usage.
Does my class need another <column> entry for the unique constraint I am adding?
I will continue to search for a solution to this problem for a time while I wait for a reply to this posting. Like I said, I do not want to code around this
as Hibernate has been very good and has taken care of all of our persistence requirements up till now.
I can post some code if it will make things clearer.
cheers,
Hibernate version: 3
Mapping documents:
<?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.tadgh2006.hibern8play" default-lazy="false" >
<class name="ParentDTO" table="tbl_Parent" >
<id name="id" column="Id" type="integer">
<generator class="native"/>
</id>
<natural-id mutable="true">
<property name="businessKey" column="BusinessKey" type="string" not-null="true" />
</natural-id>
<set name="children" inverse="true" cascade="all-delete-orphan" >
<key column="ParentId" />
<one-to-many class="ChildDTO"/>
</set>
</class>
<query name="com.tadgh2006.hibern8play.ParentDTOByBusinessKey">from ParentDTO p where p.businessKey = :businessKey</query>
</hibernate-mapping>
<?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.tadgh2006.hibern8play" default-lazy="false">
<class name="ChildDTO" table="tbl_Child" >
<id name="id" column="Id" type="integer">
<generator class="native" />
</id>
<natural-id mutable="true">
<property name="businessKey" column="BusinessKey" type="string" not-null="true" />
</natural-id>
<many-to-one name="parent" column="ParentId" class="ParentDTO" not-null="true" fetch="join" />
</class>
</hibernate-mapping>
Code between sessionFactory.openSession() and session.close():
public void save(Object object)
{
try
{
Session session = HibernateUtil.getCurrentSession();
HibernateUtil.beginTransaction();
session.update(object);
session.save(object);
HibernateUtil.commitTransaction();
}
catch ( HibernateException e )
{
handleException(e);
}
finally
{
HibernateUtil.closeSession();
}
}
Full stack trace of any exception that occurs:
Cannot insert duplicate key row in object 'tbl_Child' with unique index 'IX_tbl_Child_BusinessKey'.
Name and version of the database you are using:
Microsoft SQL Server 2000 sp4
The generated SQL (show_sql=true):
Hibernate: insert into tbl_Child (BusinessKey, ParentId) values (?, ?)