如何从保留JDO的Set中删除项目?

时间:2012-05-07 05:03:36

标签: java google-app-engine jdo datanucleus

我有一个容器对象,其中包含使用JDO 2.3在Google App Engine中保留的一组对象。我想从设置内容中删除一个对象。使用以下测试代码,remove()方法返回false,但更改不会保留,如下面的代码所示。但是,设置的基数减少了(这种行为让我感到惊讶)。如何更正此示例以从集合中删除指定的对象(在本例中为对象“one”)?

我无法在JDO文档中找到任何相关内容。平等检查和散列基于this article

日志级别调高的控制台日志转储为here(更新:这是无交易版本)。

包含事务的控制台日志转储为here

Container.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>();
    }
}

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();
    }

2 个答案:

答案 0 :(得分:1)

您的关系没有正确完成。

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

Container.java

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

Containee.java

// ...
@Persistent
private Container container;

答案 1 :(得分:1)

在经历了一个令人沮丧和沮丧的周末之后,我找到了一个解决方法:在调用Set.remove()时利用引用等式而不是 value 相等。这是代码(有趣的一点从注释“获取对持久化集合中对象的引用”):

    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框架使用对象引用而不是相等性检查来处理删除,但我没有在文档中找到任何证实或否定该假设的内容。