4

私の問題

新しい 1.7.5 GAE/J SDK を使用して、バージョン 1.x から 2.0 の GAE/J 用 DataNucleus プラグインにアプリケーションを移植しています。これにより、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) クライアント VM 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)
4

2 に答える 2

3

GAE JDO プラグインは、データストアから「gae.pk-id」/「gae.pk-name」フィールドでマークされたフィールドを読み取るときにのみ設定します (SVN トランクで検索するだけで、FetchFieldManager が唯一の場所です)。ロードされた場所 - PUT を実行するときに設定されません)。1.x で何をしたかはわかりませんが、GAE 独自のテストはすべて、1.x で行ったように 2.x でもパスします。しかし、その「機能」はとにかく標準の JDO ではないので、私にはほとんど興味がありません。

JDO はライフサイクル リスナーを提供し、簡単にpostStoreコールバックをセットアップして、オブジェクトにいくつかのフィールドを設定できます (AppEngine 固有の「機能」に依存する必要はありません)。

于 2013-02-26T09:36:41.340 に答える
2

@DataNucleusのコメントに触発されて、私は漠然と似た精神で回避策を作成しました。以下に示す回避策は私には有効ですが、根本的な問題が残っていることがわかりました。

エンコードされたキー文字列の(読み取り専用)数値IDを使用するすべての永続エンティティは、getID()回避策を使用するためにメソッドを変更する必要があります。

Javaコード

IDゲッターメソッド(前述)を次のように修正します。

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;
}
于 2013-03-05T23:58:14.107 に答える