-->
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.  [ 3 posts ] 
Author Message
 Post subject: Object Layer vs DB referential integrity
PostPosted: Thu Jun 08, 2006 11:51 am 
Newbie

Joined: Thu Jun 08, 2006 11:23 am
Posts: 2
Hello,

I have a question that may have been answered elsewhere on the forums (but if so, I couldn't find the proper keywords). Basically, I'm wondering what is the best way to enforce referential integrity using Hibernate.

If I were doing pure POJOs, I might enforce referential integrity programatically. So, for example, if I had a many-to-many bidirectional association between a Person and an Event (to use the example from the tutorial), I might code them as follows:

Code:
public class Event {
    ...
    public Set getParticipants() {
       return participants;
    }
    public void setParticipants(Set participants) {
       this.participants = participants;
    }
    public void addParticipant(Person participant) {
       if (!getParticipants().contains(participant)) {
          getParticipants().add(participant);
          participant.addEvent(this);
       }
    }
    public void removeParticipant(Person participant) {
       if (getParticipants().contains(participant)) {
          getParticipants().remove(participant);
          participant.removeEvent(this);
       }
    }
}


Likewise, I would code the Person class as follows:

Code:
public class Person {
        ...   
   public Set getEvents() {
      return events;
   }
   public void setEvents(Set events) {
      this.events = events;
   }
   public void addEvent(Event event) {
      if (!getEvents().contains(event)) {
         getEvents().add(event);
         event.addParticipant(this);
      }
   }
   public void removeEvent(Event event) {
      if (getEvents().contains(event)) {
         getEvents().remove(event);
         event.removeParticipant(this);
      }
   }
}


With this code, adding an event to a participant will turn around and add the participant to the event (and vice-versa) programmatically (i.e. in the object layer).

However, when I write code like the above, and then attempt to commit them in a transaction, I get a ConstraintViolationException because both the person and event objects are attempting to write a record with the same key values to the join table.

From experimentation, I know that if I don't write the referential integrity code in Java, the transaction will commit fine, and then after the commit, I can read the association from either side of the relationship and it will be available (i.e. the database is enforcing the referential integrity).

So...my question is, what is the proper way to go about doing this in Hibernate. Presumably, I want my object to exhibit referential integrity, but in order to have that happen (at least for a many-to-many), I need to commit them to the DB before attempting to traverse the relationships. Is this correct?

Though I don't think this is a 'bug' per se, I'm curious in feedback. For the record, here is the information on my setup

Hibernate version:
3.1

Mapping documents:
Event.hbm.xml
Code:
<hibernate-mapping>

    <class name="hello.Event" table="EVENTS">
        <id name="id" column="EVENT_ID">
            <generator class="native"/>
        </id>
        <property name="date" type="timestamp" column="EVENT_DATE"/>
        <property name="title"/>

        <set name="participants" table="PERSON_EVENT">
           <key column="EVENT_ID"/>
           <many-to-many column="PERSON_ID" class="hello.Person"/>
       </set>
    </class>

</hibernate-mapping>

Person.hbm.xml
Code:
<hibernate-mapping>

    <class name="hello.Person" table="PERSON">
        <id name="id" column="PERSON_ID">
            <generator class="native"/>
        </id>
        <property name="age"/>
        <property name="firstname"/>
        <property name="lastname"/>
       
        <set name="events" table="PERSON_EVENT">
           <key column="PERSON_ID"/>
           <many-to-many column="EVENT_ID" class="hello.Event"/>
       </set>
    </class>

</hibernate-mapping>


Code between sessionFactory.openSession() and session.close():
Code:
       Transaction tx = session.beginTransaction();

       Person aPerson = (Person) session.load(Person.class, personId);
       Event anEvent = (Event) session.load(Event.class, eventId);

       aPerson.addEvent(anEvent);

       tx.commit();



Full stack trace of any exception that occurs:
Code:
Exception in thread "main" org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
   at org.hibernate.exception.ErrorCodeConverter.convert(ErrorCodeConverter.java:74)
   at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
   at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:181)
   at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:226)
   at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:139)
   at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:274)
   at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
   at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:730)
   at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:324)
   at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:86)
   at hello.EventManager.addPersonToEvent(EventManager.java:89)
   at hello.EventManager.main(EventManager.java:33)
Caused by: java.sql.BatchUpdateException: Duplicate entry '5-5' for key 1
   at com.mysql.jdbc.ServerPreparedStatement.executeBatch(ServerPreparedStatement.java:647)
   at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:57)
   at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:174)


Name and version of the database you are using:
MySQL

The generated SQL (show_sql=true):
Code:
Hibernate: insert into PERSON_EVENT (PERSON_ID, EVENT_ID) values (?, ?)
Hibernate: insert into PERSON_EVENT (EVENT_ID, PERSON_ID) values (?, ?)

_________________
Tim Morrison
www.unity-software.com


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jun 08, 2006 2:18 pm 
Newbie

Joined: Thu Jun 08, 2006 2:03 pm
Posts: 2
One side of the relationship should be mapped with inverse="true", and the Java code should ensure that the correct add/remove actions happen on both sides of the relationship.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jun 08, 2006 3:50 pm 
Newbie

Joined: Thu Jun 08, 2006 11:23 am
Posts: 2
Thanks for the response -- I should have remembered to put the inverse flag in from the get go.

After doing so, my code mapping looks as follows:

Code:
<hibernate-mapping>

    <class name="hello.Event" table="EVENTS">
        <id name="id" column="EVENT_ID">
            <generator class="native"/>
        </id>
        <property name="date" type="timestamp" column="EVENT_DATE"/>
        <property name="title"/>

        <set name="participants" table="PERSON_EVENT" inverse="true">
           <key column="EVENT_ID"/>
           <many-to-many column="PERSON_ID" class="hello.Person"/>
       </set>
    </class>

</hibernate-mapping>


However, after doing so, I still end up with the same problem as originally described. That is, I get two INSERTs into the join table, with the same keys. Any other thoughts?

_________________
Tim Morrison
www.unity-software.com


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 3 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.