-->
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.  [ 2 posts ] 
Author Message
 Post subject: Many-To-Many update with mutable hashCode
PostPosted: Wed Jun 25, 2008 5:26 pm 
Newbie

Joined: Wed Jun 25, 2008 8:43 am
Posts: 2
Location: Austria
I'm experiencing an unique constraint violation when updating a many-to-many association if the hashCode() method of the entities is based on fields which have a different value than in the database.

What I try to do in two subsequent web requests (that means the session is closed in-between):

    Store two objects with the following data:
    Code:
    Parent:
      idField1   = 1
      dataField1 = dataParent
      systemId   = 3
      Set<Child> = {child} (many-to-many association)


    Code:
    Child:
      idField2   = 1
      dataField2 = dataChild
      systemId   = 3


    The tables PARENT, CHILD and PARENTS2CHILD are populated correctly.

    Now I try to merge and flush the previously saved objects with the following data:
    Code:
    Parent:
      idField1   = 1
      dataField1 = dataParent
      systemId   = 4                 //this value has changed
      Set<Child> = {child} (many-to-many association)


    Code:
    Child:
      idField2   = 1
      dataField2 = dataChild
      systemId   = 4                 //this value has changed


    The tables PARENT, CHILD are updated correctly. But Hibernate tries to insert a new row in the relation table PARENTS2CHILD and an Unique Constraint Violation occurs.

The reason is that the systemId is part of the hashCode generation for the Parent and Child classes (see below). This behavior is generated by AndroMDA out of our UML model automatically. So the entities are first added to a Set internally by Hibernate after the initial load from the database (with systemId=3) and during the check for necessary updates the entities with systemId=4 are considered to be missing (since they can not be found in the internal Set because their hashCode has changed).

I know that this behavior violates the contract of the Set specification, but from the view of a Hibernate user one can not know that the implementation of a by value equality/hashCode (as mentioned in the book Java Persistence with Hibernate) leads to the described problem. In this book, the by value equality/hashCode is not recommended, but also not completely forbidden, but when using many-to-many associations it seems to be problematic.

In http://www.hibernate.org/109.html and in other discussions I found very few comments on by value equality/hashCode so I think it is not used widely, am I right?

In our case, I think we will change the generation process for our Entity classes in a way that the equals and hashCode methods only depend on the database identifier, but I would be interested in your opinion about using by value equality/hashCode generated by default for all Entities in a class hierarchy which have no specified identifier(like in this case GeneralEntity, see below).

Cheers,
Jürgen


Hibernate version:
Experienced with 3.2.4.sp1 and 3.2.6.GA

Mapping documents:

GeneralEntity:
Code:
    @javax.persistence.Column(name = "SYSTEM_ID", insertable = true, updatable = true)
    public java.lang.String getSystemId()
   ...
   
   public int hashCode()
    {
        int hashCode = 0;
        hashCode = 29 * hashCode + (getSystemId() == null ? 0 : getSystemId().hashCode());

        return hashCode;
   }


Parent extends GeneralEntity:
Code:
    @javax.persistence.Id
    @javax.persistence.Column(name = "ID_FELD1", nullable = false, insertable = true, updatable = true)
    public java.lang.String getIdFeld1()
   ...

    @javax.persistence.ManyToMany(cascade = {javax.persistence.CascadeType.ALL})
    @javax.persistence.JoinTable
    (
        name = "PARENTS2CHILD",
        joinColumns = {@javax.persistence.JoinColumn(name = "PARENTS_ID_FELD1_FK", referencedColumnName = "ID_FELD1")},
        inverseJoinColumns = {@javax.persistence.JoinColumn(name = "CHILD_ID_FELD2_FK", referencedColumnName = "ID_FELD2")}
    )
    public java.util.Set<com.trinitec.samples.ejb3xfire.Child> getChild()
    ....
   
    public int hashCode()
    {
        int hashCode = super.hashCode();
        hashCode = 29 * hashCode + (getIdFeld1() == null ? 0 : getIdFeld1().hashCode());

        return hashCode;
    }   


Child extends GeneralEntity:
Code:
    @javax.persistence.Id
    @javax.persistence.Column(name = "ID_FELD2", nullable = false, insertable = true, updatable = true)
    public java.lang.String getIdFeld2()
    ...
   
    public int hashCode()
    {
        int hashCode = super.hashCode();
        hashCode = 29 * hashCode + (getIdFeld2() == null ? 0 : getIdFeld2().hashCode());

        return hashCode;
    }


Code between sessionFactory.openSession() and session.close():
Code:
emanager is injected by container:
...
            emanager.merge(parent);
            emanager.flush();
...


Full stack trace of any exception that occurs:
Code:
2008-06-25 17:26:25,703 DEBUG [org.hibernate.util.JDBCExceptionReporter] Could not execute JDBC batch update [insert into PARENTS2CHILD (PARENTS_ID_FELD1_FK, CHILD_ID_FELD2_FK) values (?, ?)]
java.sql.BatchUpdateException: ORA-00001: unique constraint (TSTJA.SYS_C00342166) violated

   at oracle.jdbc.driver.DatabaseError.throwBatchUpdateException(DatabaseError.java:343)
   at oracle.jdbc.driver.OraclePreparedStatement.executeBatch(OraclePreparedStatement.java:10656)
   at org.jboss.resource.adapter.jdbc.WrappedStatement.executeBatch(WrappedStatement.java:519)
   at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:48)
   at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:246)
   at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:237)
   at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:144)
   at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
   at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
   at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
...


Name and version of the database you are using:

Oracle 10.1.02

The generated SQL (show_sql=true):
Code:
...
2008-06-25 17:26:25,656 INFO  [STDOUT] Hibernate: select parent0_.ID_FELD1 as ID1_3_1_, parent0_.SYSTEM_ID as SYSTEM2_3_1_, parent0_.DATA_FELD1 as DATA3_3_1_, child1_.PARENTS_ID_FELD1_FK as PARENTS1_3_, child2_.ID_FELD2 as CHILD2_3_, child2_.ID_FELD2 as ID1_0_0_, child2_.SYSTEM_ID as SYSTEM2_0_0_, child2_.DATA_FELD2 as DATA3_0_0_ from PARENT parent0_ left outer join PARENTS2CHILD child1_ on parent0_.ID_FELD1=child1_.PARENTS_ID_FELD1_FK left outer join CHILD child2_ on child1_.CHILD_ID_FELD2_FK=child2_.ID_FELD2 where parent0_.ID_FELD1=?
...
2008-06-25 17:26:25,671 INFO  [STDOUT] Hibernate: update CHILD set SYSTEM_ID=?, DATA_FELD2=? where ID_FELD2=?
...
2008-06-25 17:26:25,687 INFO  [STDOUT] Hibernate: update PARENT set SYSTEM_ID=?, DATA_FELD1=? where ID_FELD1=?
...
2008-06-25 17:26:25,687 INFO  [STDOUT] Hibernate: insert into PARENTS2CHILD (PARENTS_ID_FELD1_FK, CHILD_ID_FELD2_FK) values (?, ?)
...


Debug level Hibernate log excerpt:
Code:
2008-06-25 17:26:25,687 DEBUG [org.hibernate.jdbc.ConnectionManager] skipping aggressive-release due to flush cycle
2008-06-25 17:26:25,687 DEBUG [org.hibernate.persister.collection.AbstractCollectionPersister] Deleting rows of collection: [com.trinitec.samples.ejb3xfire.Parent.child#1]
2008-06-25 17:26:25,687 DEBUG [org.hibernate.persister.collection.AbstractCollectionPersister] no rows to delete
2008-06-25 17:26:25,687 DEBUG [org.hibernate.persister.collection.AbstractCollectionPersister] Inserting rows of collection: [com.trinitec.samples.ejb3xfire.Parent.child#1]
2008-06-25 17:26:25,687 DEBUG [org.hibernate.jdbc.AbstractBatcher] about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
2008-06-25 17:26:25,687 DEBUG [org.hibernate.SQL] insert into PARENTS2CHILD (PARENTS_ID_FELD1_FK, CHILD_ID_FELD2_FK) values (?, ?)


Top
 Profile  
 
 Post subject: Re: Many-To-Many update with mutable hashCode
PostPosted: Tue Jan 10, 2012 6:07 am 
Newbie

Joined: Tue Jan 10, 2012 5:48 am
Posts: 1
Have you checked your unique constraint on the database?
It has to be defined on both columns.


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