1

JDO 2.3を使用して Google App Engine に永続化される一連のオブジェクトを含むコンテナ オブジェクトがあります。セット内容からオブジェクトを削除したい。次のテスト コードでは、remove() メソッドは false を返しますが、次のコードが示すように、変更は保持されません。ただし、セットのカーディナリティは減少します (この動作には驚かされます)。このサンプルを修正して、指定されたオブジェクト (この場合はオブジェクト「1」) をセットから削除するにはどうすればよいですか?

JDO ドキュメントで関連するものを見つけることができませんでした。等値チェックとハッシュは、この記事に基づいています。

ログ レベルを上げたコンソール ログのダンプはこちら(更新: これはトランザクションのないバージョンです)。

トランザクションを含むコンソール ログのダンプはこちらにあります。

コンテナ.java

import java.util.HashSet;
import java.util.Set;

import javax.jdo.annotations.FetchGroup;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

@PersistenceCapable(detachable = "true")
@FetchGroup(name = "withContents", members = { @Persistent(name = "contents") })
public class Container
{
    @PrimaryKey
    private String id;

    @Persistent(dependentElement = "true")
    private Set<Containee> contents;

    public Set<Containee> getContents()
    {
        return contents;
    }

    public Container(String id)
    {
        super();
        this.id = id;
        contents = new HashSet<Containee>();
    }
}

コンテナ.java

import javax.jdo.annotations.Extension;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

@PersistenceCapable(detachable = "true")
public class Containee
{
    @SuppressWarnings("unused")
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    @Extension(vendorName = "datanucleus", 
       key = "gae.encoded-pk", value = "true")
    private String id;

    @Persistent
    private String friendlyName;

    public String getFriendlyName()
    {
        return friendlyName;
    }

    public Containee(String friendlyName)
    {
        this.friendlyName = friendlyName;
    }

    @Override
    public boolean equals(Object other)
    {
        if (other instanceof Containee)
        {
            Containee that = (Containee) other;
            return this.getFriendlyName().equals(that.getFriendlyName());
        }
        return false;
    }

    @Override
    public int hashCode()
    {
        return friendlyName.hashCode();
    }
}

テスト スニペット(RemoteService の一部としてサーバー側で実行)

...

        System.out.println("Fetching...");
        Container after = pm.getObjectById(Container.class, "test");

        // prints 2
        System.out.println("Pre-remove set cardinality "
                + after.getContents().size());

        // prints "true"
        System.out.println("Post-store containment: "
                + after.getContents().contains(one));

        for (Containee e : after.getContents())
        {
            System.out.println(e.getFriendlyName());
        }

        System.out.println("Mark");
        boolean result = after.getContents().remove(one);
        System.out.println("End Mark");

        System.out
                .println("'after' object class: " + after.getContents().getClass());

        // prints "false" (!?!?)
        System.out.println("Post-store removal: " + result);

        // prints 1 (...?)
        System.out.println("Post-remove set cardinality: "
                + after.getContents().size());

...

編集:

トランザクションでスニペットをテストする

    Container before = new Container("test");

    Containee one = new Containee("one");
    Containee two = new Containee("two");
    Containee three = new Containee("three");

    before.getContents().add(one);
    before.getContents().add(two);
    before.getContents().add(three);

    // prints "true"
    System.out.println("Pre-store containment: "
            + before.getContents().contains(two));

    // prints "true"
    System.out.println("Pre-store removal: "
            + before.getContents().remove(two));

    PersistenceManager pm = pmf.getPersistenceManager();

    try
    {
        pm.makePersistent(before);
    }
    finally
    {
        pm.close();
    }

    pm = pmf.getPersistenceManager();
    pm.getFetchPlan().addGroup("withContents");

    Transaction tx = pm.currentTransaction();

    try
    {
        System.out.println("Fetching...");
        Container after = pm.getObjectById(Container.class, "test");

        // prints 2
        System.out.println("Pre-remove set cardinality "
                + after.getContents().size());

        // prints "true"
        System.out.println("Post-store containment: "
                + after.getContents().contains(one));

        for (Containee e : after.getContents())
        {
            System.out.println(e.getFriendlyName());
        }

        tx.begin();

        System.out.println("Mark");
        boolean hrm = after.getContents().remove(one);
        System.out.println("End Mark");

        tx.commit();

        System.out
                .println("'after' object class: " + after.getContents().getClass());

        // prints "false" (!?!?)
        System.out.println("Post-store removal: " + hrm);

        // prints 1 (...?)
        System.out.println("Post-remove set cardinality: "
                + after.getContents().size());

    }
    finally
    {
        System.out.println("Finalizing transaction...");
        if (tx.isActive())
        {
            System.out.println("Rolling back...");
            tx.rollback();
        }
    }

    pm.close();

    pm = pmf.getPersistenceManager();
    pm.getFetchPlan().addGroup("withContents");

    try
    {
        System.out.println("Fetching again...");
        Container after = pm.getObjectById(Container.class, "test");

        // prints 2
        System.out.println("Final set cardinality "
                + after.getContents().size());
    }
    finally
    {
        pm.close();
    }
4

2 に答える 2

1

あなたの関係は正しく行われていません。

https://developers.google.com/appengine/docs/java/datastore/jdo/relationships#Owned_One_to_Many_Relationships

コンテナ.java

// ...
@Persistent(mappedBy = "employee", dependentElement = "true")
private Set<Containee> contents;

コンテナ.java

// ...
@Persistent
private Container container;
于 2012-05-07T08:00:51.770 に答える
1

頭をひっかいてイライラした週末の後、回避策を見つけました。 Set.remove() を呼び出すときに、の等価性ではなく参照の等価性を利用することです。コードは次のとおりです (興味深いビットは、「永続化されたセット内のオブジェクトへの参照を取得する」というコメントから始まります)。

    Container before = new Container("test");

    Containee one = new Containee("one");
    Containee two = new Containee("two");
    Containee three = new Containee("three");

    before.getContents().add(one);
    before.getContents().add(two);
    before.getContents().add(three);

    pm = pmf.getPersistenceManager();

    try
    {
        pm.makePersistent(before);
    }
    finally
    {
        pm.close();
    }

    pm = pmf.getPersistenceManager();

    try
    {
        Container after = pm.getObjectById(Container.class, "test");

        // prints 3
        System.out.println("Pre-remove set cardinality "
                + after.getContents().size());

        // prints "true"
        System.out.println("Post-store containment: "
                + after.getContents().contains(one));

        //get a reference to the object in the persisted Set
        //that is value-equivalent to Containee #1
        Containee ref = null;
        for (Containee c : after.getContents())
        {
            if (c.equals(one)) ref = c;
        }

        if (ref != null)
        {
            after.getContents().remove(ref);
        }

        // prints 2
        System.out.println("Post-remove set cardinality: "
                + after.getContents().size());

    }
    finally
    {
        pm.close();
    }

    pm = pmf.getPersistenceManager();

    try
    {
        Container after = pm.getObjectById(Container.class, "test");

        // prints 2 (as expected)
        System.out.println("Final set cardinality "
                + after.getContents().size());
    }
    finally
    {
        pm.close();
    }

このコードはそれを示していませんが、操作を悲観的なトランザクションでラップすることは、同時実行の問題を回避するためにおそらく良い計画です。

この手法の成功により、DataNucleus フレームワークは等価チェックではなくオブジェクト参照を使用して削除を処理しているのではないかと疑うようになりましたが、その仮説を確認または反証するドキュメントは見つかりませんでした。

于 2012-05-14T04:36:45.317 に答える