-->
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: remove() on Hibernate list with index : Constraint violation
PostPosted: Fri May 11, 2007 8:28 am 
Newbie

Joined: Thu Apr 06, 2006 2:09 pm
Posts: 14
The problem

When you map a one-to-many association using and Hibernate indexed persistent list with a join table (so we need to use many-to-many with unique="true" as one-to-many does not handle join table) any remove on this list leads to constraint violation during database synchronisation.

Class and mapping

Code:
package org.hibernate.test.collection.list;

import java.util.List;
import java.util.ArrayList;

/**
* {@inheritDoc}
*
* @author Steve Ebersole
*/

public class ListOwner {

   private String name;
   private ListOwner parent;
   private List children = new ArrayList();

   public ListOwner() {
   }

   public ListOwner(String name) {
      this.name = name;
   }

   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }

   public ListOwner getParent() {
      return parent;
   }

   public void setParent(ListOwner parent) {
      this.parent = parent;
   }

   public List getChildren() {
      return children;
   }

   public void setChildren(List children) {
      this.children = children;
   }

}


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="org.hibernate.test.collection.list">
    <class name="ListOwner">
      <id name="name" column="NAME" type="string" />
        <many-to-one name="parent" class="ListOwner" cascade="none" update="false" insert="false" />
        <list name="children"  cascade="all, delete-orphan" table="listowners">       
            <key column="PARENT" />
            <list-index column="LIST_INDEX"/>
            <many-to-many class="ListOwner" unique="true" />
        </list>
   </class>
</hibernate-mapping>



Unit Test

Code:
package org.hibernate.test.collection.list;

import java.util.ArrayList;
import junit.framework.Test;
import org.hibernate.Session;
import org.hibernate.collection.PersistentList;
import org.hibernate.junit.functional.FunctionalTestCase;
import org.hibernate.junit.functional.FunctionalTestClassTestSuite;


/**
* Tests related to operations on a PersistentList
*
* @author Steve Ebersole
*/

public class PersistentListTest extends FunctionalTestCase {

   public PersistentListTest(String name) {
      super( name );
   }

   public String[] getMappings() {
      return new String[] { "collection/list/Mappings.hbm.xml" };
   }

   public static Test suite() {
      return new FunctionalTestClassTestSuite( PersistentListTest.class );
   }

   public void testRandomRemove() {

      ListOwner parent = new ListOwner( "root" );
      ListOwner child = new ListOwner( "c1" );
      ListOwner child1 = new ListOwner( "c2" );
      ListOwner child2 = new ListOwner( "c3" );
      ListOwner child3 = new ListOwner( "c4" );
      parent.getChildren().add( child );
      parent.getChildren().add( child1 );
      parent.getChildren().add( child2 );
      parent.getChildren().add( child3 );
      child.setParent( parent );
      child1.setParent( parent );
      child2.setParent( parent );


      Session session = openSession();
      session.beginTransaction();
      session.save( parent );
      session.flush();

      parent.getChildren().remove( 1 );
      session.update( parent );
      session.flush();
      session.getTransaction().commit();
      session.close();            

   }
}


Console ouput

Code:
Hibernate:
    delete
    from
        listowners
    where
        PARENT=?
        and LIST_INDEX=?
Hibernate:
    update
        listowners
    set
        elt=?
    where
        PARENT=?
        and LIST_INDEX=?
14:08:41,470  WARN JDBCExceptionReporter:77 - SQL Error: 0, SQLState: null
14:08:41,470 ERROR JDBCExceptionReporter:78 - Batch entry 0 update listowners set elt=c3 where PARENT=root and LIST_INDEX=1 was aborted.  Call getNextException to see the cause.
14:08:41,470  WARN JDBCExceptionReporter:77 - SQL Error: 0, SQLState: 23505
14:08:41,470 ERROR JDBCExceptionReporter:78 - ERROR: duplicate key violates unique constraint "listowners_elt_key"
14:08:41,477 ERROR AbstractFlushingEventListener:301 - Could not synchronize database state with session
org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
   at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:71)
   at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
   at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:253)
   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)
   at org.hibernate.test.collection.list.PersistentListTest.testRandomRemove(PersistentListTest.java:93)
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
   at java.lang.reflect.Method.invoke(Method.java:597)
   at junit.framework.TestCase.runTest(TestCase.java:154)
   at org.hibernate.junit.functional.FunctionalTestCase.runTest(FunctionalTestCase.java:101)
   at junit.framework.TestCase.runBare(TestCase.java:127)
   at org.hibernate.junit.UnitTestCase.runBare(UnitTestCase.java:34)
   at junit.framework.TestResult$1.protect(TestResult.java:106)
   at junit.framework.TestResult.runProtected(TestResult.java:124)
   at junit.framework.TestResult.run(TestResult.java:109)
   at junit.framework.TestCase.run(TestCase.java:118)
   at junit.framework.TestSuite.runTest(TestSuite.java:208)
   at org.hibernate.junit.functional.FunctionalTestClassTestSuite.runTest(FunctionalTestClassTestSuite.java:100)
   at junit.framework.TestSuite.run(TestSuite.java:203)
   at org.hibernate.junit.functional.FunctionalTestClassTestSuite.run(FunctionalTestClassTestSuite.java:69)
   at org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:128)
   at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Caused by: java.sql.BatchUpdateException: Batch entry 0 update listowners set elt=c3 where PARENT=root and LIST_INDEX=1 was aborted.  Call getNextException to see the cause.
   at org.postgresql.jdbc2.AbstractJdbc2Statement$BatchResultHandler.handleError(AbstractJdbc2Statement.java:2497)
   at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1299)
   at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:349)
   at org.postgresql.jdbc2.AbstractJdbc2Statement.executeBatch(AbstractJdbc2Statement.java:2559)
   at com.p6spy.engine.logging.P6LogPreparedStatement.executeBatch(P6LogPreparedStatement.java:329)
   at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:48)
   at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:246)
   ... 28 more


Sql Profiler output (for the remove instruction)

Prepared SQL: delete from listowners where PARENT=? and LIST_INDEX=? SQL Statement: delete from listowners where PARENT='root' and LIST_INDEX=3

Prepared SQL: update listowners set elt=? where PARENT=? and LIST_INDEX=? SQL Statement: update listowners set elt='c4' where PARENT='root' and LIST_INDEX=2

Conclusion

I think that the attribute unique="true" of the many-to-many element in the mapping is not taken into account during the synchronisation with the database.

Should I report to JIRA ?


Top
 Profile  
 
 Post subject:
PostPosted: Fri May 11, 2007 10:32 am 
Newbie

Joined: Thu Apr 06, 2006 2:09 pm
Posts: 14
Just to be clear: the problem came up when I was using the following annotations to map a one-to-many association with a join table :

@OneToMany
@JoinTable
@IndexColumn


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.