无法使用GAE / J DataNucleus插件版本2.1.2获取新创建的JDO持久实体的ID

我的问题

我正在使用新的1.7.5 GAE / J SDK将我的应用程序从版本1.x移植到GAE / J的DataNucleus插件的2.0版本。 这将我的JDO版本从2.3更改为3.0.1。 我的持久化实体类具有类型为编码字符串的主键,以及对该对象的数字ID的只读访问权限。 每个实例都是其实体组的唯一成员(子级和父级仅通过数字ID链接)。

以前,我已经能够创建并持久化新的MyEntity实例,然后立即访问其数字ID以存储在父MyEntity实例的子ID列表中。

现在我发现新实例的数字ID在持久化后不会立即可用 – 即使它是生成并存储的并且稍后可用。

我的问题

在创建对象和持久性之后,我有什么办法可以立即恢复对数字ID的访问吗?

“jdoconfig.xml”配置提取

       [...]  

持久化实体类代码提取

 @PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true") public class MyEntity implements Serializable { private static final long serialVersionUID = 1L; // No setter for this read-only data member @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) @Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true") private String sEncodedKey; // No setter for this read-only data member @Persistent @Extension(vendorName="datanucleus", key="gae.pk-id", value="true") private Long loID; @Persistent private Long loParentID; // // Other persistent data members // public Long getID() { return loID; } // // Other getters and setters // } 

持久性代码包括3个记录点

 /** * Create a new entity. * @param loParentID * The ID of the entity, * a new child of which is to be created. * @param sChildName * The name of the new child to be created. * @return * The created entity child, * or null if the operation was carried out unsuccessfully. */ public static MyEntity createEntityChild(Long loParentID, String sChildName) { MyEntity meResult = null; MyEntity mePersistedChild = null; PersistenceManagerFactory pmf = DataExchange.getPersistenceManagerFactory(); // My own method PersistenceManager pm = pmf.getPersistenceManager(); Transaction tx = pm.currentTransaction(); try { tx.begin(); MyEntity meChild = new MyEntity(); meChild.setParentID(loParentID); meChild.setName(sChildName); meChild.setActive(true); mePersistedChild = pm.makePersistent(meChild); // "Touch" data member not in the default fetch group ArrayList liChildIDs = mePersistedChild.getChildIDs(); if (liChildIDs != null) liChildIDs.size(); if (mePersistedChild != null) g_logger.log(Level.FINE, String.format( "Pre-commit: mePersistedChild.getID() = %d," + " mePersistedChild.getEncodedKey() = \"%s\".", mePersistedChild.getID(), mePersistedChild.getEncodedKey())); tx.commit(); if (mePersistedChild != null) g_logger.log(Level.FINE, String.format( "Post-commit: mePersistedChild.getID() = %d," + " mePersistedChild.getEncodedKey() = \"%s\".", mePersistedChild.getID(), mePersistedChild.getEncodedKey())); } finally { try { if (tx.isActive()) // Because of an exception, say tx.rollback(); } finally { pm.close(); } } if (mePersistedChild != null) g_logger.log(Level.FINE, String.format( "Post-pm-close: mePersistedChild.getID() = %d," + " mePersistedChild.getEncodedKey() = \"%s\".", mePersistedChild.getID(), mePersistedChild.getEncodedKey())); [...] return meResult; } 

开发服务器日志输出

 24-Feb-2013 13:28:02 [...].MyEntityBusiness createMyEntityChild FINE: Pre-commit: mePersistedChild.getID() = null, mePersistedChild.getEncodedKey() = "agttYXJrZXQtdHJlZXISCxIMSXRlbUNhdGVnb3J5GAUM". 24-Feb-2013 13:28:03 [...].MyEntityBusiness createMyEntityChild FINE: Post-commit: mePersistedChild.getID() = null, mePersistedChild.getEncodedKey() = "agttYXJrZXQtdHJlZXISCxIMSXRlbUNhdGVnb3J5GAUM". 24-Feb-2013 13:28:03 [...].MyEntityBusiness createMyEntityChild FINE: Post-pm-close: mePersistedChild.getID() = null, mePersistedChild.getEncodedKey() = "agttYXJrZXQtdHJlZXISCxIMSXRlbUNhdGVnb3J5GAUM". 24-Feb-2013 13:28:07 com.google.appengine.api.datastore.dev.LocalDatastoreService$PersistDatastore persist INFO: Time to persist datastore: 141 ms 

JDO增强版validation

构建过程成功输出片段:

 datanucleusenhancer: 09:33:00,531 (main) INFO [DataNucleus.Enhancer] - DataNucleus Enhancer for API "JDO" 09:33:01,125 (main) INFO [DataNucleus.Enhancer] - DataNucleus Enhancer (version 3.1.1) : Enhancement of classes DataNucleus Enhancer (version 3.1.1) : Enhancement of classes 09:33:03,281 (main) INFO [DataNucleus.Enhancer] - Writing class file "[Path]\MyEntity.class" with enhanced definition [... (N entries in all)] 09:33:04,046 (main) INFO [DataNucleus.Enhancer] - DataNucleus Enhancer completed with success for [N] classes. Timings : input=1922 ms, enhance=984 ms, total=2906 ms. Consult the log for full details DataNucleus Enhancer completed with success for [N] classes. Timings : input=1922 ms, enhance=984 ms, total=2906 ms. Consult the log for full details 

软件环境

  • Web服务器:Google App Engine for Java 1.7.5版
  • Web框架:Apache Wicket 6.5.0
  • Java版本:1.6.0_39; Java HotSpot(TM)客户端VM 20.14-b01
  • GAE / J DataNucleus插件版本:2.1.2
  • DataNucleus Access Platform版本:3.1.3
  • JDO版本:3.0.1
  • 操作系统:在x86上运行的Microsoft Windows XP 5.1
  • IDE:NetBeans 7.2(内部版本201207171143)

GAE JDO插件只有在从数据存储区读入标有该字段的字段时才设置“gae.pk-id”/“gae.pk-name”字段(只需在SVN中继中搜索,FetchFieldManager是唯一的地方)它被加载的地方 – 当它执行PUT时它不会设置它。 不知道它在1.x中做了什么,但是所有GAE自己的测试都在2.x中传递,就像在1.x中那样。 但是那个“特征”无论如何都不是标准的JDO,所以对我没什么兴趣。

JDO提供了生命周期监听器 ,你可以很容易地设置一个postStore回调并在你的对象中设置一些字段(而不是依赖于AppEngine特定的“function”)。

受@ DataNucleus评论的启发,我以一种模糊的相似精神进行了解决。 下面的解决方法对我有用,但我发现我的根本问题仍然存在。

使用编码键字符串的(只读)数字ID的所有持久性实体都需要更改其getID()方法以使用解决方法。

Java代码

我将我的ID getter方法(前面给出)修改为如下:

 public Long getID() { Long loResult = DataExchange.getIDFromEKSIfIDIsNull(loID, sEncodedKey); return loResult; } 

我的DataExchange类有新方法:

 import com.google.appengine.api.datastore.Key; import com.google.appengine.api.datastore.KeyFactory; /** * Get the ID supplied, or get it from the encoded key string if the ID is * null. * 
* This method is necessary since JDO version 3.0.1 introduces a long delay * between entity first persistence and ID availability using the DataNucleus * GAE primary key ID plug-in. * @param loID * The persistent entity ID. * This may be null if the entity has been persisted for the * first time but its generation is delayed (a big hello to JDO version * 3.0.1). * @param sEncodedKey * The persistent entity encoded key string. * This should be not null if the entity has been persisted. * @return *
    *
  • * If the persistent entity ID supplied is not null * then return it *
  • *
  • * else if the encoded key string is not null then extract * the ID and return it *
  • *
  • * else return null. *
  • *
*/ public static Long getIDFromEKSIfIDIsNull(Long loID, String sEncodedKey) { Long loResult = null; if (loID != null) loResult = loID; else if (sEncodedKey != null) { Key key = KeyFactory.stringToKey(sEncodedKey); if (key != null) { long loIDFromEKS = key.getId(); loResult = Long.valueOf(loIDFromEKS); } } return loResult; }