-->
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.  [ 1 post ] 
Author Message
 Post subject: Swapping elements in OneToMany list
PostPosted: Thu Jun 14, 2007 5:12 pm 
Newbie

Joined: Wed Feb 08, 2006 8:48 am
Posts: 6
Hello,

I'm unable to swap elements of unidirectional list supported with additional database table.

Model

Code:
@Entity
public class Unit {
   private Long         id;
   private List<Action>   actions   = new LinkedList<Action>();

   @Id
   @GeneratedValue
   public Long getId() {
      return id;
   }

   public void setId( Long id ) {
      this.id = id;
   }

   @OneToMany(cascade = CascadeType.ALL)
   @IndexColumn(name = "idx")
   @JoinTable(name = "UnitActions", joinColumns = { @JoinColumn(name = "unit_id") }, inverseJoinColumns = @JoinColumn(name = "action_id"))
   @Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
   public List<Action> getActions() {
      return actions;
   }

   public void setActions( List<Action> actions ) {
      this.actions = actions;
   }
}


Code:
@javax.persistence.Entity
public class Action {
   private Long   id;
   private String   name;

   public Action() {

   }

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

   @NotNull
   public String getName() {
      return name;
   }

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

   @Id
   @GeneratedValue
   public Long getId() {
      return id;
   }

   public void setId( Long id ) {
      this.id = id;
   }
}


Code between sessionFactory.openSession() and session.close():

Code:
Unit unit = (Unit) session.load( Unit.class, id );

// HERE IS THE PROBLEM
Collections.swap( unit.getActions(), 0, 1 );

session.persist( unit );


SQL statements for MySQL
Code:
DEBUG (2007-06-14) 22:32.39:906 [SQL]  update UnitActions set action_id=? where unit_id=? and idx=?
DEBUG (2007-06-14) 22:32.39:906 [AbstractBatcher]  preparing statement
DEBUG (2007-06-14) 22:32.39:906 [LongType]  binding '36' to parameter: 1
DEBUG (2007-06-14) 22:32.39:906 [LongType]  binding '18' to parameter: 2
DEBUG (2007-06-14) 22:32.39:906 [IntegerType]  binding '0' to parameter: 3
DEBUG (2007-06-14) 22:32.39:906 [LongType]  binding '35' to parameter: 1
DEBUG (2007-06-14) 22:32.39:906 [LongType]  binding '18' to parameter: 2
DEBUG (2007-06-14) 22:32.39:906 [IntegerType]  binding '1' to parameter: 3


As you can see, these two updates cannot work without violating database constrains.

Stacktrace
Code:
ERROR (2007-06-14) 22:32.39:937 [AbstractFlushingEventListener]  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.impl.SessionImpl.managedFlush(SessionImpl.java:338)
   at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
   at com.mobilebox.hibtest1.FirstTestCase.testOne(FirstTestCase.java:73)
   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 junit.framework.TestCase.runBare(TestCase.java:127)
   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 junit.framework.TestSuite.run(TestSuite.java:203)
   at org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:130)
   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: Duplicate entry '35' for key 2
   at com.mysql.jdbc.ServerPreparedStatement.executeBatch(ServerPreparedStatement.java:657)
   at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:48)
   at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:246)
   ... 26 more


What I can see is tha Hibernate tries to swap list elements by switching action_ids while keeping idx values of particular rows.
This leads to constraint violations as the first update causes a single action_id to occur in two rows.

Sample Maven project to illustrate the problem:
http://mefiu.wlkp.org/test/hibtest1.zip
File size 10kB, run with:
Code:
mvn clean install


Wouldn't it be possible to switch idx values and leave action_ids as they are ?
Rhis would require the Hibernate list to be aware of the fact that the element has been removed and inserted at different list position.

Any current solution requires deep invasion into DAOs code as you are unable to switch elements without removing first, flushing, repositioning second, adding a FRESH new one into second's position. This may of course yield big problems if deleting a child has cascade effects.

Could you please confirm this error with my sample source project?

Hibernate version:
core: 3.2.4.sp1 + annotations 3.3.0.ga
Name and version of the database:
MySQL 5.0.3
Test case for HSQLDB 1.8.0.7


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

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.