为什么Google数据存储控制台的行为与GAE Java数据存储库不同?
我有一个GoogleAppEngine+Java应用程序,它已经愉快地运行了很多年(使用JDO+数据存储进行持久化),我在手动更新GoogleDataStore控制台中实体的属性方面没有问题(偶尔也不情愿) 最近(可能是最近2-3个月),我注意到一个行为上的变化,它破坏了我们的应用程序。我不知道到底出了什么问题,也不知道我们如何处理 所以我的问题是: 为什么它的表现不同?我能做些什么 让我首先尝试解释我看到的行为,然后展示我的最小可能的复制测试用例 假设您有一个简单的持久性类:为什么Google数据存储控制台的行为与GAE Java数据存储库不同?,java,google-app-engine,google-cloud-datastore,Java,Google App Engine,Google Cloud Datastore,我有一个GoogleAppEngine+Java应用程序,它已经愉快地运行了很多年(使用JDO+数据存储进行持久化),我在手动更新GoogleDataStore控制台中实体的属性方面没有问题(偶尔也不情愿) 最近(可能是最近2-3个月),我注意到一个行为上的变化,它破坏了我们的应用程序。我不知道到底出了什么问题,也不知道我们如何处理 所以我的问题是: 为什么它的表现不同?我能做些什么 让我首先尝试解释我看到的行为,然后展示我的最小可能的复制测试用例 假设您有一个简单的持久性类: @Persist
@PersistenceCapable
public class Account implements Serializable {
@Persistent private ShortBlob testShortBlob;
@Persistent private String name;
// ...etc...
}
如果我过去通过DataStoreWeb控制台编辑名称,它将按预期工作,名称字段将更改,其他一切都将正常工作
我现在看到的行为是,通过控制台保存实体后,我无法再在JDO中查询和加载实体,我得到:
java.lang.ClassCastException: com.google.appengine.api.datastore.Blob cannot be cast to com.google.appengine.api.datastore.ShortBlob
它指向一些底层数据存储更改,这意味着ShortBlob字段的类型正在从ShortBlob更改为Blob(即使我没有通过控制台对该字段进行编辑)
此测试用例将复制该问题:
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
// this one really is a ShortBlob - will load fine in JDO
Entity account = new Entity("Account", "123");
account.setProperty("name", "Test Name");
account.setUnindexedProperty("testShortBlob", new ShortBlob("blah".getBytes()));
datastore.put(account);
// this one really is not a ShortBlob, its a blob - it will fail for the same reason I am seeing in production.
account = new Entity("Account", "124");
account.setProperty("name", "Test Name 2");
account.setUnindexedProperty("testShortBlob", new Blob("blah".getBytes()));
datastore.put(account);
// then load the entity via JDO
try {
accountFromJdo = pm.getObjectById(Account.class, key);
} catch (Exception ex) {
System.out.println("We get here, the object won't load with the ClassCast Exception");
}
这就是问题所在,但为什么通过云数据存储控制台进行保存会将ShortBlob更改为Blob
我目前的解决方法是在数据存储控制台中将ShortBlob字段设置为null,这样就可以加载实体。但如果blob中的数据很重要,那就糟了
更新:
我在这方面做了更多的测试,使用低级别的JSON API,看看在通过控制台保存实体之前和之后,是否可以在原始JSON响应中发现差异。好消息是,我可以
在通过控制台编辑实体之前,通过JDO App Engine界面保存到数据存储的shortBlob字段如下所示:
},
"testShortBlob": {
"blobValue": "tNp7MfsjhdfjkahsdvfkjhsdvfIItWyzy6glmIrow4WWhRPbhQ/U+MGX3opVvpxu"
},
但是,如果我进入数据存储控制台,编辑实体(保持blob字段不变,编辑一个不相关的字段,例如name。现在,当我运行相同的查询时,我得到:
},
"testShortBlob": {
"blobValue": "tNp7MfsjhdfjkahsdvfkjhsdvfIItWyzy6glmIrow4WWhRPbhQ/U+MGX3opVvpxu",
"excludeFromIndexes": true
},
细微的差别,但我认为这很重要,根据ShortBlob
索引,而Blob
不索引
因此,我想我现在的问题是:为什么通过云数据存储控制台编辑实体会改变blob字段的索引状态?感谢您提出的详细问题和调试。这似乎有点可疑。我将确保分配给正确的团队 就解决办法而言: 您注意到的JSON API提供了多种帮助,使其易于访问
可以使用该API以事务方式读取/修改/写入实体。在您的情况下,它将允许您执行所需的转换。或者,通过JDO进行修改也可以工作。我认为这是一个错误,数据存储控制台在保存时不应更改属性的索引状态,因此我提出了一个问题在这里的跟踪器中:这个错误已经被修复,同时我发现我能够使用数据存储API(而不是JDO API)修复损坏的实体,方法是使用blob中的字节将值设置回ShortBlob,比如:.setProperty(prop,new ShortBlob((blob)value).getBytes());