【问题标题】:Updating objects in GAE在 GAME 中更新对象
【发布时间】:2011-09-20 14:20:04
【问题描述】:

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

我正在使用 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();
    }
}

【问题讨论】:

    标签: java google-app-engine jdo datanucleus


    【解决方案1】:

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

    JDOHelper.makeDirty(obj, "myFieldName");
    

    完成更新后,JDO 将注册更改并在数据存储中更新。

    【讨论】:

    • 嘿,这正是我所需要的。现在,我有了有史以来最好的解决方案,我的所有对象只有一种方法可以保存/更新数据存储区中的对象。只是为了让所有人都知道,您在这里有 2 个选项: - 或者您将 markDirty 放在每个不起作用的集合中,我不建议这样做。我正在使用 GXT,而 GXT 的工作方式以某种方式忽略了 JDO。 - 或者你把它放在服务器端的保存方法中,我这样做并推荐。您可以检查差异,但我发现将所有字段标记为脏更容易。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      是的,问题在于交易。如果您想在给定时间保证修改的持久性,您必须提交/刷新事务。我有一个使用 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

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

      【讨论】:

      • 好吧,我正在使用 begin 和 commit 方法来避免这种情况,但即使它失败了。无论有没有提交,对于我在这里展示的所有方法,我都会得到与之前相同的结果。
      • 尝试使用 setter 方法而不是代码中执行自省/反射的字段来设置 bean 中的值
      • 但问题是如果我这样做,那么我的所有模型都没有通用方法。正如我所说,我可以为我拥有的每个模型创建一个更新方法,但我想尽我所能避免这种情况。
      • 对不起,我提醒,在你为 (Field f : ob.getClass().getDeclaredFields()) { 做的通用代码中,做 Method m .... 然后使用setter 方法来设置值而不是字段...
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-07-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多