我有一个容器对象,其中包含使用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();
}
答案 0 :(得分:1)
您的关系没有正确完成。
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框架使用对象引用而不是相等性检查来处理删除,但我没有在文档中找到任何证实或否定该假设的内容。