在GAE中更新对象

时间:2011-09-20 14:20:05

标签: java google-app-engine jdo datanucleus

我遇到了一个无法解决的问题。我试图在网上搜索解决方案,但我没有找到任何通用解决方案。我想在数据存储区中更新一个对象,无论它是什么类。为此,这是我用于项目的代码

我正在使用DataNucleus和Google AppEngine。

这是我的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>

我的PMF课程

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();
    }
}

我的BaseModel类,它是项目所有模型的超类

@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;
        }
    }
}

这是我要保存的课程(Project

@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;
}

为此,我尝试了几件事。下面的方法成功保存了Project的一个新实例,但是当我调用更新已经分离的对象时,只更新字段 deadline ,其他属性没有更新(但是,我进入调试模式,检查其他属性是否已更改,是的,是,但只有截止日期得到保存)。

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

所以,我尝试了以下代码

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();
    }
}

它有效。好的,它以这种方式工作,但我需要一个通用的方法来处理我的所有模型,所以我尝试了这个

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();
    }
}

但它根本不起作用,没有任何东西得到保存,但是当我手动输出System.out属性时,它们会被更改。我试着没有pm.makePersistent(ob);而没有运气。我不知道该怎么办。我在这个项目中有120个继承自BaseModel的模型,我找不到一种方法来进行适用于我的模型的更新。

-----------编辑-----------

感谢您的回答。这是我现在的解决方案。当然,printStrackTree会离开那里。

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();
    }
}

3 个答案:

答案 0 :(得分:4)

JDO字节码增强方法不会检测到使用反射来更新字段。如果您想直接更新字段(无论是通过字段还是通过反射),那么您可以调用

JDOHelper.makeDirty(obj, "myFieldName");

进行更新后,然后更改将由JDO注册,并在数据存储区中进行更新。

答案 1 :(得分:1)

您必须在使用反射时使用JDOHelper,以确保您的字段被标记为脏。

答案 2 :(得分:0)

是的,问题在于交易。如果您希望在给定时间具有GUARANTEED持久性修改,则必须提交/刷新事务。我有一个使用JPA的GAE应用程序,但我遇到了完全相同的问题。

为了避免使用样板代码,我为我的DAO创建了一个基类,它可以解析一个Method对象,启动一个事务,执行该方法然后提交它(或者将其回滚)。然后在我的dao中,我传递一个方法处理程序和这个baseDao的参数,以便进行(半)自动事务处理。

如果您认为这些代码有用,可以查看代码:

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