ludoch / datanucleus-appengine

Automatically exported from code.google.com/p/datanucleus-appengine
0 stars 0 forks source link

one-to-many relationship with list-ordering delete problem #313

Open GoogleCodeExporter opened 8 years ago

GoogleCodeExporter commented 8 years ago
The problem is, that the ArrayList's delegate and backingstore are not 
consistent. Delegate contains the list sorted as per list-ordering clause, 
whereas backingstore fetches the list from the datastore which is not sorted. 

So on delete, the backingstore gets the index of the item to be deleted from 
delegate list, and datastore has incorrect entry for that index.

User class
public class User {

    @Persistent
    private String firstName;

   @Persistent
    private String lastName;

    @PrimaryKey
    private Key key; 

    @Persistent
    private String email;

    @Persistent(defaultFetchGroup="true")
    @Element(dependent = "true")
    @Order(extensions = @Extension(vendorName="datanucleus",key="list-ordering", value="userEmail asc, name asc"))
    private List<Item> items;

   //getters and setters

}

Item Class
@PersistenceCapable
public class Item implements DataModel{

    @Persistent
    private String userEmail;

    @Persistent
    private String name;

    @PrimaryKey
    @Persistent
    private Key key;

   //getters and setters
}

Now add the following two items 
{"userEmail":"test2@gmail.com","name":"zzz"}
{"userEmail":"test2@gmail.com","name":"hhh"}

Note list ordering for name field is asc.  The datastore has following entry. 

agpjb3N0cGVydXNlchgLEgRVc2VyIg50ZXN0QGdtYWlsLmNvbQw     14  test@gmail.com 
    test@gmail.com      [User("test@gmail.com")/Item("zzz"), 
User("test@gmail.com")/Item("hhh")]

The list is ordered correctly when the data is retrieved, so when I query the 
user, I get item "hhh" followed by "zzz" which is fine. 
{"items":[{"type":"item","userEmail":"test@gmail.com","name":"hhh"},   
          {"type":"item","userEmail":"test@gmail.com","name":"zzz"}]}

The problem is with delete, when I delete "hhh", it ends up deleting entry 
"zzz" from the datastore. 

       try{     
            tx.begin();
            user.getItems();
            user.removeItem(item);          
            pm.makePersistent(user);
            tx.commit();
        } catch (Exception e) {
            throw new WebApplicationException(e,
                    Response.Status.INTERNAL_SERVER_ERROR);
        } finally {
            if (tx.isActive()) {
                tx.rollback();
            }
            pm.close();
        }

Here is the removeItem code

    public synchronized void removeItem(Item item) {

        for (Iterator<Item> iter = items.iterator(); iter.hasNext();) {
            Item storedItem = iter.next();

            if (storedItem.getKey().equals(item.getKey())) {
               iter.remove();
            }
        }

this is what is happening
When the user class is loaded, FetchFieldManager.java orders the item list as 
per the ordering clause. 
so the User object has sco.backed.ArrayList whose delegate has the item list is 
sorted by name. 

When iter.remove is called above, it calls ArrayList.remove and this calls the 
backingStore (FKListStore) remove with the index of the item to be deleted. 
This index does not match the datastore index because the list is not sorted in 
the datastore and hence it ends up deletng the wrong entry.

Original issue reported on code.google.com by mona.ah...@gmail.com on 21 Feb 2013 at 10:33