-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 
Author Message
 Post subject: Unique constraint violation in child collection
PostPosted: Fri Jul 07, 2006 12:01 pm 
Newbie

Joined: Tue Apr 19, 2005 3:16 pm
Posts: 18
Hey there, everyone. I'm having an interesting problem with a child collection, and I was hoping someone here would have some input on what I'm doing wrong. I've simplified down to two classes that can reproduce the behavior.

In short, I have a Thing. The Thing has a collection of past Status, each with a statusName, startDate and endDate. The current Status of the Thing is represented by the Status without an endDate.

The problem occurs when I load an existing Thing and try to change its current status. I take the current Status object, set an end date on it, and add a new Status object without an end date. When I persist, Hibernate appears to attempt to insert the new Status before updating the end date on the old Status, which violates the unique constraint that says you can only have one Status with a null end date.

Any suggestions on what I can do to make Hibernate understand that it needs to update the existing records before inserting new ones would be greatly appreciated. :)

In the database, I have the following:

THING table:
Code:
CREATE TABLE THING
(
  THING_ID  NUMBER(15)
);

ALTER TABLE THING ADD
(
  PRIMARY KEY  (THING_ID)
);


STATUS table:
Code:
CREATE TABLE STATUS
(
  STATUS_ID   NUMBER(15),
  THING_ID    NUMBER(15)                        NOT NULL,
  STATUSNAME  VARCHAR2(15 BYTE)                 NOT NULL,
  STARTDATE   DATE                              NOT NULL,
  ENDDATE     DATE
);

ALTER TABLE STATUS ADD
(
  PRIMARY KEY (STATUS_ID)
);

ALTER TABLE STATUS ADD
(
  CONSTRAINT STATUS_UK1
  UNIQUE (THING_ID, STARTDATE, STATUSNAME)
);

ALTER TABLE STATUS ADD
(
  CONSTRAINT STATUS_END_UK
  UNIQUE (THING_ID, ENDDATE)
);

ALTER TABLE STATUS ADD
(
  FOREIGN KEY (THING_ID) REFERENCES THING (THING_ID)
);


Mapping documents:

Thing.hbm.xml:
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="test">

   <class name="Thing" table="Thing">
      <id name="id" column="thing_id" type="java.lang.Long">
         <generator class="org.hibernate.id.SequenceGenerator">
            <param name="sequence">thing_id_seq</param>
         </generator>
      </id>
      
      <set name="statusHistory" cascade="all, delete-orphan">
         <key column="thing_id" not-null="true"/>
         <one-to-many class="Status"/>
      </set>
   </class>
   
</hibernate-mapping>


Status.hbm.xml:
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="test">

   <class name="Status" table="Status">
      <id name="id" column="status_id" type="java.lang.Long">
         <generator class="org.hibernate.id.SequenceGenerator">
            <param name="sequence">status_id_seq</param>
         </generator>
      </id>
      
      <many-to-one name="thing" class="Thing" column="thing_id" insert="false" update="false" not-null="true"/>
      <property name="statusName" type="string"/>
      <property name="startDate" type="java.util.Date"/>
      <property name="endDate" type="java.util.Date"/>
   </class>
   
</hibernate-mapping>


Here's the test code I'm running:
Code:
Session sess = PersistenceUtil.getSession("test");
Query q = sess.createQuery("FROM Thing as t where t.id = 1");
Thing thing = (Thing)q.uniqueResult();

thing.setStatus("Old");
Transaction tx = sess.beginTransaction();
sess.saveOrUpdate(thing);
tx.commit();


And the relevant method in Thing.java:
Code:
public void setStatus(String statusName)
{
   Status oldStat = null;
   boolean found = false;
   Iterator i = statusHistory.iterator();
   while (i.hasNext() && !found)
   {
      oldStat = (Status)i.next();
      if (oldStat.getEndDate() == null)
         found = true;
   }
   
   oldStat.setEndDate(new Date());
   Status newStat = new Status(statusName);
   newStat.setThing(this);
   statusHistory.add(newStat);
}


And, last but not least, here is the generated SQL and error that results:
Code:
Hibernate: select thing0_.thing_id as thing1_ from apps.Thing
   thing0_ where thing0_.thing_id=1
Hibernate: select statushist0_.thing_id as thing2_1_, statushist0_.status_id
   as status1_1_, statushist0_.status_id as status1_0_,
   statushist0_.thing_id as thing2_0_0_, statushist0_.statusName as
   statusName0_0_, statushist0_.startDate as startDate0_0_,
   statushist0_.endDate as endDate0_0_ from
   apps.Status statushist0_ where statushist0_.thing_id=?
Hibernate: select apps.status_id_seq.nextval from dual
Hibernate: insert into apps.Status (statusName, startDate, endDate,
   thing_id, status_id) values (?, ?, ?, ?, ?)
WARN  [org.hibernate.util.JDBCExceptionReporter] SQL Error: 1,
   SQLState: 23000
ERROR [org.hibernate.util.JDBCExceptionReporter] ORA-00001: unique
   constraint (APPS.STATUS_END_UK) violated

WARN  [org.hibernate.util.JDBCExceptionReporter] SQL Error: 1,
   SQLState: 23000
ERROR [org.hibernate.util.JDBCExceptionReporter] ORA-00001: unique
   constraint (APPS.STATUS_END_UK) violated


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 07, 2006 4:02 pm 
Newbie

Joined: Tue Apr 19, 2005 3:16 pm
Posts: 18
By the way, I'm certain the problem is in the order of the SQL execution. I made the troublesome constraint deferable, and all works well. Problem is, our DBA has Views(TM) about deferable constraints, so that will be our absolute, last-ditch solution.

Anyone have any suggestions?


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 07, 2006 4:08 pm 
Expert
Expert

Joined: Tue Dec 28, 2004 7:02 am
Posts: 573
Location: Toulouse, France
Actually, a primary key should never change, and that's what you want to do, isn't it ?

_________________
Baptiste
PS : please don't forget to give credits below if you found this answer useful :)


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 07, 2006 5:31 pm 
Newbie

Joined: Tue Apr 19, 2005 3:16 pm
Posts: 18
batmat wrote:
Actually, a primary key should never change, and that's what you want to do, isn't it ?


In the real world, status will be things like "Pending Approval", "Approved", "Ended", etc., and there will be a date stamp for when the status was changed (ended), and who changed it. So, the primary key for the Status, if I wasn't using a surrogate key for the examples, would be Thing ID, Status Name, and Start Date. End Date and EndedBy are audit columns.

-- Kin


Top
 Profile  
 
 Post subject: Unique Constraint
PostPosted: Fri Oct 27, 2006 6:47 am 
Newbie

Joined: Tue Oct 05, 2004 9:21 am
Posts: 7
Location: Dublin, Ireland
Hi,

Did you manage to find a solution to this? I am experiencing the same problem, and my unique columns are not part of the primary key.
I have a component class which contains a collection of person identifiers. The primary key is on the id and idx value. There exists a unique constraint on the identifier type and identifier value. If I attempt to remove one of three entries for example, I get a uniqueConstraint. As you say, it's as though it inserts the new set before removing the old?

Anyone any ideas?

Thanks,
BW


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.