我在Google应用引擎中部署了一个应用程序。当我在更新该实体后立即通过id获取实体时,我得到的数据不一致。我使用JDO 3.0访问应用引擎数据存储区。
我有一个实体员工
@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() * */
}
最初,当我创建员工时,我没有设置字段salary和email。
我更新Employee实体以便稍后添加工资和电子邮件。更新工作正常,数据将持久保存到appengine数据存储区中。但是,当我立即尝试通过id获取相同的员工实体时,我有时会得到过时的数据,其中薪水和电子邮件为空。我用于创建和获取员工实体的代码如下所示。
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中明确地将数据存储读取一致性设置为strong。
<?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>
答案 0 :(得分:5)
如果您使用的是High Replication数据存储区,则设置读取策略并不能确保所有读取都非常一致,这些只能用于祖先查询。来自文档;
API还允许您明确设置强一致性策略,但此设置不会产生任何实际效果,因为无论策略如何,非祖先查询始终始终保持一致。
https://cloud.google.com/appengine/docs/java/datastore/queries#Java_Data_consistency https://cloud.google.com/appengine/docs/java/datastore/jdo/overview-dn2#Setting_the_Datastore_Read_Policy_and_Call_Deadline
请查看有关Structuring Data for Strong Consistency的文档,首选方法是使用缓存层来提供数据。
我注意到你正在使用get by ID,不确定,但是“按键获取”应该是非常一致的,即使对于HR数据存储区(reference),你能否尝试将其更改为基于键?密钥是使用id和实体种类和祖先构建的。
答案 1 :(得分:3)
我有一个建议,但你不会那样:使用低级API并在使用GAE时忘记JDO / JPA。
就像@asp所说的那样,获取ID应该是非常一致的,但是GAE JDO插件似乎对我有误。不幸的是,迁移到JPA也没有帮助我(在这里更多:JDO transactions + many GAE instances = overriding data)。此外,如果我将任何类注释为@PersistenceAware,Eclipse会变得疯狂,并在无限循环中增强类。另外,当使用带有嵌入式类和缓存的@PersistenceCapable类时,我遇到了很多问题(没有缓存它工作正常)。
嗯,关键是,我认为使用低级API会更快 - 你确切知道发生了什么,它似乎按预期工作。您可以将实体视为地图,只需一点自编包装代码,它就像是一个非常有趣的选择。我运行了一些测试,并且使用低级API我没有通过它们,而传递它与JDO / JPA是不可能的。我正在将整个应用程序从JDO迁移到低级API。这是耗时的,但不能无限期地等待来自GAE团队的一些神奇的解决方案或错误修正。
此外,在撰写GAE JDO时,我觉得......独自一人。如果你有java,甚至是android的问题,其他数千人已经遇到过这个问题,在stackoverflow上询问它并获得了大量有效的解决方案。在这里,你们都是独立的,所以尽可能使用低级API,你们肯定会发生什么。虽然迁移看起来很恐怖,但是我认为你在迁移到低级API时浪费的时间少于处理GAE JDO / JPA。我不会写它来捏造开发GAE JDO / JPA的团队或冒犯他们,我确信他们会尽力而为。但是:
使用GAE的人并不多,比如一般的Android或Java,
将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。
这就是为什么我认为最好只使用最基本的方法来访问和操作数据。
祝你好运,祝你一切顺利。答案 2 :(得分:0)
在实体类中添加@Cacheable(value = "false")
。该问题将得到解决。
上述问题主要归因于JDO缓存。因此,如果我们禁用缓存,JDO将从数据存储区读取数据。
或者您可以在jdoconfig.xml中禁用L2缓存。
引用链接:http://www.datanucleus.org/products/accessplatform_3_0/jdo/cache.html