-->
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: Struts 2 and replacing collections
PostPosted: Wed Aug 06, 2008 11:19 pm 
Newbie

Joined: Tue Dec 07, 2004 8:15 pm
Posts: 10
Location: Adelaide Australia
This is more of a solution than a problem reproduced here to help someone else.

Situation:
You have a one to many association mapped as a collection. You have a Struts2 type converter for the collection property that constructs a new collection which the Struts2 parameters interceptor then calls the setter with. The collection is mapped with cascade=all-delete-orphan to ensure that when objects are deleted from the collection they are deleted from the database.

Problem:
When replacing the collection the old entries are not being deleted from the database but rather the new entries are being added to what is already there leading to duplicate rows.

What is happening:
When Hibernate initializes the object containing the collection it calls the setter method with a Hibernate enhanced collection that tracks when items are removed from the collection. When Struts2 calls the setter with the collection created in the type converter it replaces the Hibernate enhanced collection which then goes out of scope and is garbage collected meaning no deletions are tracked. When Hibernate comes to commit the transaction it sees an un-enhanced collection which it assumes means that it needs to insert the entries.

Incorrect Solution 1:
The first solution I tried was to change the setter method on the class to delete all of the entries in the current collection and then add the new ones rather than replacing the collection class. This doesn't work because Hibernate itself calls the setter when loading the class to put the enhanced collection in. By not replacing the default empty collection that is created when the class loads the class never ends up with an enhanced collection and so never tracks the deletes. This leads to the same result.

Incorrect Solution 2:
The second solution I tried was to initialize the collection to an empty collection on object load and then only replace it if its size was 0. If the size wasn't 0 I deleted the old objects and added the new ones to the existing collection. This worked OK for some of the cases but in the situation when the collection was empty when Hibernate loaded it the code did a replacement of the collection which turned out to be a bad thing. This caused the empty enhanced collection to be garbage collected which shouldn't matter to the final result - there is nothing to delete after all - but Hibernate places checks for enhanced collections that manage all-delete-orphan relationships and throws exceptions when they go out of scope. Not a good solution.

Working solution:
What I needed was a way to distinguish between when Hibernate was setting the collection and when the Struts2 Parameters Interceptor was. I didn't want to do anything like trying to examine the runtime class of the collection being passed to the setter as that would introduce ugly Hibernate dependencies into my POJO. As described above checking the size was out also. The solution was that if the collection was null I replaced it and if it was not null I updated it. Sample code for the setter below:
Code:
  /**
   * @param objects the objects to set
   */
  public void setObjects(List<TrafficUnitObject> objects)
  {

    if (this.objects == null) //already initialised
    {
      this.objects = objects;
    }
    else
    {
      while (this.objects.size() > 0)
      {
        this.objects.remove(0);
      }
      this.objects.addAll(objects);
    }
  }

If the object is empty and Hibernate is initializing it the collection will be null so it will be replaced by the Hibernate enhanced collection. If the object has been initialized with an enhanced collection then the collection will be non null and we will delete the existing objects and set the new objects. Because the collection is enhanced at this point Hibernate will track the additions and deletions. This means that the collection must be initialized to null on object creation and that clients of the class need to be aware that this is possible for objects that have not yet been saved to the database.

_________________
Peter Kelley


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.