Updating objects in GAE

拥有回忆 提交于 2019-12-07 20:37:07

问题


I have a problem that I can't be able to solve. I've tried to search on the web for solutions, but I didn't find any generic solution. I want to update an object, whatever it's class may be, in the datastore. For that, here's the code I'm using for the project

I'm using DataNucleus and Google AppEngine.

Here's my jdoconfig.xml

<?xml version="1.0" encoding="utf-8"?>
<jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig">

   <persistence-manager-factory name="transactions-optional">
       <property name="javax.jdo.PersistenceManagerFactoryClass"
           value="org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/>
       <property name="javax.jdo.option.ConnectionURL" value="appengine"/>
       <property name="javax.jdo.option.NontransactionalRead" value="true"/>
       <property name="javax.jdo.option.NontransactionalWrite" value="true"/>
       <property name="javax.jdo.option.RetainValues" value="true"/>
       <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/>
   </persistence-manager-factory>
</jdoconfig>

My PMF class

public final class PMF {
    private static final PersistenceManagerFactory pmfInstance =
        JDOHelper.getPersistenceManagerFactory("transactions-optional");

    private PMF() {}

    public static PersistenceManagerFactory get() {
        return pmfInstance;
    }

    public static PersistenceManager getPersistenceManager() {
        return pmfInstance.getPersistenceManager();
    }
}

My BaseModel class, wich is a superclass for all the models of the project

@Inheritance(strategy = InheritanceStrategy.SUBCLASS_TABLE)
@PersistenceCapable(detachable = "true", identityType = IdentityType.APPLICATION)
public abstract class BaseModel implements Serializable {

    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    protected Key id;

    public boolean equals(Object obj) {
        try {
            return obj instanceof BaseModel ? this.getId().getId() == ((BaseModel) obj).getId().getId() : false;
        } catch (Exception e) {
            return false;
        }
    }
}

Here's the class (Project) I want to save

@PersistenceCapable(detachable = "true", identityType = IdentityType.APPLICATION)
public class Project extends BaseModel implements BeanModelTag {

    private static final long serialVersionUID = 3318013676594981107L;

    @Persistent
    private String name;

    @Persistent
    private String description;

    @Persistent
    private Date deadline;

    @Persistent
    private Integer status;

    @Persistent
    private Key manager;

    @Persistent
    private Set<Key> team;
}

For that, I tried several things. This method below saves a NEW instance of Project with success, but when I call to for updating an already detached object, only the field deadline is updated, the other attributes aren't updated (and yet, I went in debug mode to check out if the other attributes where changed, and yes, they were, but only deadline get's saved).

public void save(BaseModel object) {
    PersistenceManager pm = PMF.getPersistenceManager();
    try {
        pm.makePersistent(object);
    } finally {
        pm.close();
    }
}

So, I tried the following code

public void update(Project object) {
    PersistenceManager pm = PMF.get().getPersistenceManager();
    try {
        pm.currentTransaction().begin();

        Project p = pm.getObjectById(Project.class, object.getId());
        p.setName(object.getName());
        p.setDeadline(object.getDeadline());
        p.setDescription(object.getDescription());
        p.setTeam(p.getTeam());
        p.setStatus(object.getStatus());

        pm.currentTransaction().commit();
    } catch (Exception e) {
        e.printStackTrace();
        pm.currentTransaction().rollback();
    } finally {
        pm.close();
    }
}

And it worked. Ok, it works in this way, but I need a generic method for all my models, so I tried this

public void update(BaseModel object) {
    PersistenceManager pm = PMF.get().getPersistenceManager();
    try {
        pm.currentTransaction().begin();

        BaseModel ob = pm.getObjectById(object.getClass(), object.getId());

        for (Field f : ob.getClass().getDeclaredFields()) {
            if (!f.toString().contains("final")) {
                f.setAccessible(true);
                for (Field g : object.getClass().getDeclaredFields()) {
                    g.setAccessible(true);
                    if (f.getName().equals(g.getName())) {
                        f.set(ob, g.get(object));
                    }
                    g.setAccessible(false);
                }
            }
            f.setAccessible(false);
        }

        pm.makePersistent(ob);
        pm.currentTransaction().commit();
    } catch (Exception e) {
        e.printStackTrace();
        pm.currentTransaction().rollback();
    } finally {
        pm.close();
    }
}

But it doesn't work at all, nothing gets saved, and yet when I System.out the attributes manually, they are changed. I tried with and without pm.makePersistent(ob); with no luck. I don't know what to do. I have 120 models in this project that inherits from BaseModel and I can't find a way to make an update that works with my model.

----------- Edit -----------

Thanks for the answer. Here's my solution for now. Of course, that printStrackTree will get out of there.

public void update(BaseModel object) {
    PersistenceManager pm = PMF.get().getPersistenceManager();
    try {
        pm.currentTransaction().begin();

        BaseModel ob = pm.getObjectById(object.getClass(), object.getId());

        for (Field f : ob.getClass().getDeclaredFields()) {
            if (!Pattern.compile("\\bfinal\\b").matcher(f.toString()).find()) {
                f.setAccessible(true);
                for (Field g : object.getClass().getDeclaredFields()) {
                    g.setAccessible(true);
                    if (f.getName().equals(g.getName())) {
                        f.set(ob, g.get(object));
                        JDOHelper.makeDirty(ob, f.getName());
                    }
                    g.setAccessible(false);
                }
                f.setAccessible(false);
            }
        }

        pm.makePersistent(object);
        pm.currentTransaction().commit();
    } catch (Exception e) {
        e.printStackTrace();
        pm.currentTransaction().rollback();
    } finally {
        pm.close();
    }
}

回答1:


Using reflection to update fields will not be detected by JDO bytecode enhanced methods. If you want to update fields direct (whether by field or by reflection) then you could call

JDOHelper.makeDirty(obj, "myFieldName");

after doing your update, and then the change will be registered by JDO and hence updated in the datastore.




回答2:


You must use the JDOHelper while using reflection to make sure your fields get marked as dirty.




回答3:


Yep, the issues is with transactions. If you want to have a GUARANTEED persistence of a modification at a given time, you have to commit/flush the transaction. I have a GAE app that uses JPA but I've had the exact same issue.

In order to avoid the boiler plate code, I've made a base class for my DAO's that thakes a Method object, start a transaction, exectures that method and then commits it (or rolls it back). Then in my dao's I pass a method handler and the arguments to this baseDao in order to have (semi) automatic transaction handling.

you can check out the code if you think it's somthing you'd find useful:

BaseDAO : http://myprojects2.googlecode.com/svn/trunk/javakata_project/Javakata_v2/src/com/appspot/javakata6425/server/dao/BaseDAO.java

ArticleDAO : http://myprojects2.googlecode.com/svn/trunk/javakata_project/Javakata_v2/src/com/appspot/javakata6425/server/dao/ArticleDAO.java



来源:https://stackoverflow.com/questions/7486710/updating-objects-in-gae

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!