-1

I'm having issues while trying to update objects with datanucleus (JDO). The problem instead of updating an existing object, it creates new ones along with new embedded data.

This is my Tracker.java:

@PersistenceCapable(detachable="true")
@FetchGroup(name="itemsList", members={@Persistent(name="items")})
@Version(strategy=VersionStrategy.VERSION_NUMBER)
public class Tracker implements java.io.Serializable {

    private static final long serialVersionUID = 2765740961462495537L;

    @Persistent
    protected String nombre;

    @Persistent(embedded="true")
    protected Item item;

    @Persistent(embeddedElement="true")
    @Element(types=Item.class, dependent="true")
    protected Collection<Item> items;

    public Tracker() {
        this.setNombre("");
        Item unItem = new Item();
        unItem.setNombre("Item principal");
        this.setItem(unItem);
        this.setItems(new HashSet<Item>());
    }

    public String getNombre() {
        return this.nombre;
    }

    public void setNombre(String unNombre) {
        this.nombre = unNombre;
    }

    public Item getItem() {
        return this.item;
    }

    public void setItem(Item unItem) {
        this.item = unItem;
    }

    public Collection<Item> getItems() {
        return this.items;
    }

    public void setItems(Collection<Item> unosItems) {
        this.items = unosItems;
    }

    public void addItem(Item unItem) {
        if (this.canAddItem(unItem)) this.getItems().add(unItem);
    }

    private Collection<String> getItemsNames() {
        Collection<String> names = new  ArrayList<String>();
        for (Item i : this.getItems()) names.add(i.getNombre());
        return names;
    }

    private boolean canAddItem(Item unItem) {
        if (this.getItemsNames().contains(unItem.getNombre())) return false;
        return true;
    }   

}

This is Item.java:

@PersistenceCapable(detachable="true",embeddedOnly="true")
@Version(strategy=VersionStrategy.VERSION_NUMBER)
public class Item implements java.io.Serializable {

    private static final long serialVersionUID = 2865740961462495537L;  

    @Persistent
    protected String nombre;

    public Item() {
        this.setNombre("");
    }
    public Item(String unNombre) {
        this.setNombre(unNombre);
    }

    public String getNombre(){
        return this.nombre;
    }

    public void setNombre(String unNombre){
        this.nombre = unNombre;
    }

}

This is the method that updates a Tracker object (only tracker's nombre attribute):

public static void guardarCambiosSobreDetachedTracker(){
        Tracker unTracker = getDetachedTracker();
        unTracker.setNombre("Nuevo nombre 2");
        printer.printTrackerFieldsStatus(unTracker);
        PersistenceManager pm = PMF.get().getPersistenceManager();
        //pm.setCopyOnAttach(false);
        Transaction tx = pm.currentTransaction();   
        try {   
            tx.begin();
            pm.getFetchPlan().addGroup("itemsList");
            pm.getFetchPlan().setFetchSize(3);

            System.out.println("ANTES DEL PERSIST: " + unTracker);
            pm.makePersistent(unTracker);
            tx.commit();

            System.out.println("DESPUES: " + unTracker);
        } finally {
            if (tx.isActive()) tx.rollback(); 
            pm.close();
        }
        printer.printTrackerFieldsStatus(unTracker);
    }

public static Tracker getDetachedTracker(){
    PersistenceManager pm = PMF.get().getPersistenceManager();  
    Transaction tx = pm.currentTransaction();
    Tracker detachedTracker = null;
    try {   
        tx.begin();
            Query q = pm.newQuery(Tracker.class);
            @SuppressWarnings("unchecked")
            List<Tracker> results = (List<Tracker>) q.execute();
            if (results.size() == 1) {
                pm.getFetchPlan().addGroup("itemsList");
                pm.getFetchPlan().setFetchSize(3);
                detachedTracker = (Tracker)pm.detachCopy(results.get(0));
                pm.detachCopyAll(results.get(0).getItems());
            } 
        //tx.commit();
    } finally {
        if (tx.isActive()) tx.rollback(); 
        pm.close();
    }

    return detachedTracker;
}

Why is it that when calling commit, instead of updating dettached embedded objects, it creates new ones besides existing embedded objects?

  • and are those objects "detached" ? by calling JDOHelper.getObjectState you can see the state. By looking at the log you can see the state (as well as WHAT IS HAPPENING) – Neil Stockton Jul 03 '15 at 06:33

1 Answers1

1

When you update the Tracker instance, you have to use the same PersistenceManager instance which you used to get the Tracker instance. In addition, PersistenceManager automatically detects changes made on the Tracker instance and the changes are committed to the database when appropriate, so you don't have to call makePersistent() method.

Note that makePersistent() method always inserts a new record. If what you want to do is update, you must not call makePersistent() method.

Takahiko Kawasaki
  • 15,920
  • 8
  • 53
  • 98
  • 1
    *makePersistent* does not always insert a new record. If the passed in object is managed then it does nothing (and the update is processed automatically prior to that call, since setter calls are intercepted by the bytecode enhancement). If the passed in object is detached then it attaches it. – Neil Stockton Jul 08 '15 at 17:49