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.  [ 8 posts ] 
Author Message
 Post subject: Changing an order of list items
PostPosted: Wed Apr 11, 2007 4:45 pm 
Newbie

Joined: Wed Feb 08, 2006 8:48 am
Posts: 6
I have a problem with changing an order of items in the Hibernate managed collection (List).

There is an object called Zone that has some actions connected.

======================

Code:
@Entity
public class Zone extends Persistent {
  ...
  @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
  @IndexColumn(name = "idx")
  @JoinTable(name = "zone_action", joinColumns = { @JoinColumn(name = "zone_id") }, inverseJoinColumns = @JoinColumn(name = "action_id"))
  public List<Action> getActions() {
    return actions;
  }
  ...
}

@Entity
public class Action extends Persistent {
  ...
  private int priority;
  private String name;
  ...
}


======================

What I want to do is simply move a single Action up in the order of a list:

Code:
public static <T extends Persistent> T moveUp( List<T> list, T object ) {
  if ( EntityHelper.isEntityInCollection( object.getId(), list ) ) {
    int oldIndex = list.indexOf( object );
    if ( ( 0 == oldIndex ) || ( -1 == oldIndex ) )
      return null;

    int newIndex = oldIndex - 1;
    T tmpObject = (T) list.get( newIndex );
    list.set( newIndex, object );
    list.set( oldIndex,    tmpObject );
  }
  return null;
}


======================

What I get while persisting the 'zone' is:

======================

Hibernate: update zone_action set action_id=? where zone_id=? and idx=?

Code:
[WARN] JDBCExceptionReporter - SQL Error: 1062, SQLState: 23000
[ERROR] JDBCExceptionReporter - Duplicate entry '11' for key 2
[ERROR] AbstractFlushingEventListener - Could not synchronize database
state with session <org.hibernate.exception.ConstraintViolationException: Could
not execute JDBC batch update>org.hibernate.exception.ConstraintViolationException:
Could not execute JDBC batch update


======================

which means Hibernate tries to update action_id insted of updating 'idx' column.
That is why unique constrain is violated.

======================

Questions are:

is my method moveUp() for changing object position in a list proper?
is there any better (more simple) method of achieving this?
is there any method for changing index column value in a OneToMany association?


Top
 Profile  
 
 Post subject:
PostPosted: Wed Apr 11, 2007 5:36 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
Your implementation looks ok, and the code in PersistentList looks like it should handle what you're trying. I do something like that, but instead of two sets, I use one remove then one add. It works, and I don't have to remember any other objects in the list, like you do.

BTW, why do you have a return from moveUp? It only ever returns null...

_________________
Code tags are your friend. Know them and use them.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Apr 11, 2007 6:36 pm 
Expert
Expert

Joined: Tue Jul 11, 2006 10:21 am
Posts: 457
Location: Columbus, Ohio
You can just use Collections routines to perform these types of operations.

e.g.
Code:
public static <T extends Persistent> T moveUp( List<T> list, T object ) {
  if ( EntityHelper.isEntityInCollection( object.getId(), list ) ) {
    int oldIndex = list.indexOf( object );
    if ( ( 0 == oldIndex ) || ( -1 == oldIndex ) )
      return null;

    int newIndex = oldIndex - 1;
    Collections.swap(list, newIndex, oldIndex);
  }
  return null;
}


It looks like you are doing this in a DAO. Since there is no dependency on data access, you might be better off implementing this in your domain classes. The EntityHelper call is probably redundant, since you already have a check when you do the list.indexOf call.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 12, 2007 3:35 am 
Newbie

Joined: Wed Feb 08, 2006 8:48 am
Posts: 6
As suggested by Ananasi:
Code:
Collections.swap(list, newIndex, oldIndex);

is the simplfication of what I'm trying to do.
It still throws the same unique constraint violation error.

I've done some more research on this one.
Here is what I found:

1)
When moveUp() method invokes this:
Code:
actions.remove( action );

hibernate does:
Code:
delete from zone_action where zone_id=? and idx=?


2)
When moveUp() method invokes that:
Code:
actions.add( newIndex, action );

hibernate does:
Code:
update zone_action set action_id=? where zone_id=? and idx=?


3)
When moveUp() method invokes the combination of them both (as suggested by tenwit):
Code:
actions.remove( action );
actions.add( newIndex, action );

hibernate surprisingly does:
Code:
Hibernate: update zone_action set action_id=? where zone_id=? and idx=?


What it means, Hibernate is first of all updating the newly inserted object,
and perhaps then, it'll remove the old one.
Unfortunately, there will not be any 'delete from...', because first 'update...' will violate the unique constraint.

Any suggestions how to force Hibernate to first delete old object and then insert new one?

============

To tenwit:
returning null in the method is somehow redundant.
Yes, you are right, it should be a void the method is returning, just to control the flow of the method.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 12, 2007 9:56 am 
Expert
Expert

Joined: Tue Jul 11, 2006 10:21 am
Posts: 457
Location: Columbus, Ohio
The Collections.swap method always works for me, but I have little experience using a join table with a one-to-many relationship. I'm sure that is introducing the complexity. Where does the "idx" column live?


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 12, 2007 10:29 am 
Newbie

Joined: Wed Feb 08, 2006 8:48 am
Posts: 6
Idx column is a part of a 'zone_action' table:

Code:
create table zone_action (
   zone_id bigint not null,
   action_id bigint not null,
   idx integer not null,
   primary key (zone_id, idx),
   unique (action_id)) type=InnoDB;


that is created basing on the following collections annotations:

Code:
@Entity
public class Zone extends extends Persistent {

   private List<Action>   actions   = new LinkedList<Action>();

   @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
   @IndexColumn(name = "idx")
   @JoinTable(name = "zone_action", joinColumns = { @JoinColumn(name = "zone_id") }, inverseJoinColumns = @JoinColumn(name = "action_id"))
   public List<Action> getActions() {
      return actions;
   }
   ...
}


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 12, 2007 10:37 am 
Expert
Expert

Joined: Tue Jul 11, 2006 10:21 am
Posts: 457
Location: Columbus, Ohio
Odd, shouldn't the composite key on the zone_action table be primary_key(zone_id,action_id)?


Top
 Profile  
 
 Post subject:
PostPosted: Mon Apr 16, 2007 10:01 am 
Newbie

Joined: Wed Feb 08, 2006 8:48 am
Posts: 6
Finally, I've managed to solve the problem (or at least - to overcome it):

I force the Hibernate to execute SQL statements in a required order by using:

Code:
getSession().flush();

after each block of statements that don't conflict with each other,
but have to be executed before the next statement can be done.


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