我在Google App Engine中部署了一个应用程序。更新实体后,立即通过ID获取实体时,我得到的数据不一致。我正在使用JDO 3.0来访问App Engine数据存储。

我有一个实体员工

@PersistenceCapable(detachable = "true")
public class Employee implements Serializable {

    /**
     *
     */
    private static final long serialVersionUID = -8319851654750418424L;
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY, defaultFetchGroup = "true")
    @Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true")
    private String id;
    @Persistent(defaultFetchGroup = "true")
    private String name;
    @Persistent(defaultFetchGroup = "true")
    private String designation;
    @Persistent(defaultFetchGroup = "true")
    private Date dateOfJoin;
    @Persistent(defaultFetchGroup = "true")
    private String email;
    @Persistent(defaultFetchGroup = "true")
    private Integer age;
    @Persistent(defaultFetchGroup = "true")
    private Double salary;
    @Persistent(defaultFetchGroup = "true")
    private HashMap<String, String> experience;
    @Persistent(defaultFetchGroup = "true")
    private List<Address> address;


    /**
      * Setters and getters, toString() * */

}

最初,当我创建员工时,我没有设置薪水和电子邮件字段。

我更新了Employee实体以在以后添加薪水和电子邮件。更新工作正常,数据被持久保存到appengine数据存储区中。但是,当我立即尝试通过id获取相同的员工实体时,有时会得到陈旧的数据,其中薪金和电子邮件为null。下面提供了我用于创建和获取员工实体的代码。
    public Employee create(Employee object) {
        Employee persObj = null;
        PersistenceManager pm = PMF.get().getPersistenceManager();
        Transaction tx = null;
        try {
            tx = pm.currentTransaction();
            tx.begin();

            persObj = pm.makePersistent(object);

            tx.commit();
        } finally {

            if ((tx != null) && tx.isActive()) {
                tx.rollback();
            }

            pm.close();
        }

        return persObj;
    }


    public Employee findById(Serializable id) {

        PersistenceManager pm = PMF.get().getPersistenceManager();

        try {
            Employee e = pm.getObjectById(Employee.class, id);

            System.out.println("INSIDE EMPLOYEE DAO : " + e.toString());
            return e;

        } finally {

            pm.close();

        }
    }


    public void update(Employee object) {
        PersistenceManager pm = PMF.get().getPersistenceManager();
        Transaction tx = null;
        try {
            tx = pm.currentTransaction();
            tx.begin();
            Employee e = pm.getObjectById(object.getClass(), object.getId());
            e.setName(object.getName());
            e.setDesignation(object.getDesignation());
            e.setDateOfJoin(object.getDateOfJoin());
            e.setEmail(object.getEmail());
            e.setAge(object.getAge());
        e.setSalary(object.getSalary());
            tx.commit();
        } finally {
            if (tx != null && tx.isActive()) {
                tx.rollback();
            }

            pm.close();
        }
    }

我将空闲实例的数量设置为5,并且一次运行着大约8个实例。当我检查各种实例的日志时,便发现了这一点。

为什么某些实例为请求提供服务时,我为什么会得到过时的数据。我可以保证,如果获取请求是由最初处理更新请求的实例处理的,那么我总是会获取更新后的数据。但是,当其他实例处理提取请求时,可能会返回陈旧数据。我已在jdoconfig.xml中将数据存储读取一致性明确设置为强。
<?xml version="1.0" encoding="utf-8"?>
<jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig_3_0.xsd"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig http://java.sun.com/xml/ns/jdo/jdoconfig_3_0.xsd">

   <persistence-manager-factory name="transactions-optional">
       <property name="javax.jdo.PersistenceManagerFactoryClass"
           value="org.datanucleus.api.jdo.JDOPersistenceManagerFactory"/>
       <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"/>
       <property name="datanucleus.appengine.singletonPMFForName" value="true"/>
       <property name="datanucleus.appengine.datastoreEnableXGTransactions" value="true"/>
       <property name="datanucleus.query.jdoql.allowAll" value="true"/>
       <property name="datanucleus.appengine.datastoreReadConsistency" value="STRONG" />

   </persistence-manager-factory>
</jdoconfig>

最佳答案

我有一个建议,但是您不会那样做:专门使用低级API,并且在使用GAE时会忘记JDO/JPA。

就像@asp所说的那样,按ID获取应该是高度一致的,但是GAE JDO插件对我来说似乎很麻烦。不幸的是,对我而言,迁移到JPA也没有帮助(更多信息请参见JDO transactions + many GAE instances = overriding data)。另外,如果我将任何类都注释为@PersistenceAware,则Eclipse会发疯,并在无限循环中增强这些类。另外,在将@PersistenceCapable类与嵌入式类配合使用并进行缓存时,我遇到了很多问题(不进行缓存就可以正常工作)。

好吧,关键是,我认为使用低级API会更快一些-您确切地知道发生了什么,而且似乎按预期工作。您可以将Entity像Map一样对待,并带有一些自行编写的包装代码,这似乎是一个非常有趣的选择。我运行了一些测试,并且使用低级API可以毫无问题地通过它们,而无法通过JDO/JPA进行测试。我正在将整个应用程序从JDO迁移到低级API。这很耗时,但要无限期地等待GAE团队提供一些神奇的解决方案或错误修正。

另外,在编写GAE JDO时,我感到……很孤独。如果您在使用Java甚至是android时遇到问题,那么已经有成千上万的人遇到了这个问题,请在stackoverflow上进行询问,并获得大量有效的解决方案。在这里,您全靠自己,所以请尽可能使用低级API,并且可以确定发生了什么。尽管迁移看起来很可怕而且费时,但我认为与GAE JDO/JPA相比,迁移到低级API所花费的时间更少。我不会写这篇文章来挤伤开发GAE JDO/JPA的团队,也不会冒犯他们,我相信他们会尽力而为。但:

  • 一般而言,与Android或Java相比,使用GAE的人并不多
  • 将GAE JDO/JPA与多个服务器实例一起使用并不像您想的那样简单明了。像我这样的开发人员希望尽快完成工作,看一些示例,阅读一些文档-不进行详细研究,阅读简短的教程,而开发人员有问题,他想在stackoverflow和获得快速帮助。如果您在Android上做错了任何事情,无论它是复杂的还是简单的错误,都可以轻松获得帮助。使用GAE JDO/JPA并不是那么容易。我花在GAE JDO文章,教程和文档上的时间比我想要的要多得多,即使它看起来很基础,我也没有做我想做的事情。如果我只是使用低级API而没有尝试使用JDO的快捷方式(是的,我认为JDO可以节省我的时间),那将会快得多。
  • Google比Java更加关注Python GAE。在许多面向所有读者的文章中,专门提供Python代码和提示,此处提供快速示例:http://googlecloudplatform.blogspot.com/2013/12/best-practices-for-app-engine-memcache.html或此处:https://cloud.google.com/developers/articles/balancing-strong-and-eventual-consistency-with-google-cloud-datastore/。我已经注意到了这一点,甚至在开始开发之前,但是我想与我的Android客户端共享一些代码,所以我选择了Java。即使我有扎实的Java背景并且即使我现在确实共享一些代码,但如果我能回到过去并再次选择,我还是会选择Python。

  • 这就是为什么我认为最好仅使用最基本的方法来访问和操作数据的原因。

    祝你好运,祝你一切顺利。

    关于java - 从Google App Engine数据存储区获取的不一致,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/26377347/

    10-10 06:08