【问题标题】:Unable to get ID of newly-created JDO persistent entity using GAE/J DataNucleus plug-in version 2.1.2使用 GAE/J DataNucleus 插件版本 2.1.2 无法获取新创建的 JDO 持久实体的 ID
【发布时间】:2013-02-10 12:18:43
【问题描述】:

我的问题

我正在使用新的 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”配置提取

<persistence-manager-factory name="big-table">
  <property
   name="javax.jdo.PersistenceManagerFactoryClass"
   value="org.datanucleus.api.jdo.JDOPersistenceManagerFactory"
  />
  <property name="datanucleus.DetachAllOnCommit" value="true"/>
  <property name="javax.jdo.option.NontransactionalRead" value="true"/>
  <property name="javax.jdo.option.NontransactionalWrite" value="true"/>
  <property
   name="datanucleus.appengine.autoCreateDatastoreTxns"
   value="true"
  />
  [...]
</persistence-manager-factory>

持久化实体类代码提取

@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 <code>null</code> 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<Long> 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 增强版本验证

构建过程成功,输出片段:

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) 客户端虚拟机 20.14-b01
  • GAE/J DataNucleus 插件版本:2.1.2
  • DataNucleus 访问平台版本:3.1.3
  • JDO 版本:3.0.1
  • 操作系统:在 x86 上运行的 Microsoft Windows XP 5.1 版
  • IDE:NetBeans 7.2(内部版本 201207171143)

【问题讨论】:

    标签: java google-app-engine google-cloud-datastore jdo datanucleus


    【解决方案1】:

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

    JDO 提供lifecycle listener,您可以轻松设置 postStore 回调并在其中设置对象中的某些字段(并且不依赖于 AppEngine 特定的“功能”)。

    【讨论】:

    • 感谢您的回答。我将用我的方式更新这个线程。
    • 感谢有关生命周期侦听器的提示。对我来说可悲的是,在我的 StoreLifecycleListener#postStore(InstanceLifecycleEvent event) 中使用 PersistenceCapable#jdoGetObjectId() 返回的 ID 是编码的密钥字符串,而不是它的数字 ID(这是我所追求的)。我已经在code.google.com/p/googleappengine/issues/detail?id=8925 提出了 GAE 问题 8925(保存后不再能够立即获取编码的密钥字符串主键 ID)。
    • 错误的问题跟踪器(不是 Google 回复的)code.google.com/p/datanucleus-appengine/issues/list 显然你可以通过使用 EntityUtils.getKeyForObject 来获得密钥(以及数字)......当然你会必须提取 ExecutionContext(通过将 PM 强制转换为 JDOPersistenceManager 等),但这很容易做到。
    • 谢谢。我在 DataNucleus-AppEngine 上提出了我的(重复)问题作为问题 314(保存后不再能够立即使用“datanucleus gae.pk-id”获取编码的密钥字符串主键 ID):code.google.com/p/datanucleus-appengine/issues/detail?id=314。我将用我如何处理你的代码提示来更新这个线程。 (我还将询问有关重新发布 DataNucleus-AppEngine 问题跟踪器的问题。
    【解决方案2】:

    受到@DataNucleus 的 cmets 的启发,我以一种类似的精神进行了解决。下面列出的解决方法对我有用,但我发现我的根本问题仍然存在。

    使用编码键字符串的(只读)数字 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
     * <code>null</code>.
     * <br/>
     * 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 <code>null</code> 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 <code>null</code> if the entity has been persisted.
     * @return
     *   <ul>
     *     <li>
     *       If the persistent entity ID supplied is not <code>null</code>
     *       then return it
     *     </li>
     *     <li>
     *       else if the encoded key string is not <code>null</code> then extract
     *       the ID and return it
     *     </li>
     *     <li>
     *       else return <code>null</code>.
     *     </li>
     *   </ul>
     */
    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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-26
      • 2016-11-16
      • 2011-02-15
      • 2011-10-30
      • 1970-01-01
      相关资源
      最近更新 更多