更新Datanucleus中的JDO M-N关系不会持续存在

时间:2014-11-28 10:03:58

标签: many-to-many jdo datanucleus

我一直在探索Datanucleus框架,并尝试了文档中提供的一些示例。我使用Sets陷入了M-N关系示例。当我尝试从Set中删除一个对象时,它将从Set中删除,但是一旦我持久化容器对象,就不会删除连接表中的条目。这导致我的容器对象仍然保持被移除的对象。

我有一个暴露问题的单元测试:

    @Test
public void testMNRelation() {
    final Product product = new Product();
    product.setName("Product 1");
    product.setPrice(100);

    final Product product2 = new Product();
    product2.setName("Product 2");
    product2.setPrice(130);

    final Supplier supplier = new Supplier();
    supplier.setName("Supplier 1");

    Set<Supplier> suppliers = product.getSuppliers();
    suppliers.add(supplier);

    Set<Product> products = supplier.getProducts();
    products.add(product2);

    final PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory("MyStore");
    PersistenceManager pm = pmf.getPersistenceManager();

    Transaction tx = pm.currentTransaction();
    try {
        tx.begin();
        pm.makePersistent(product);
        pm.makePersistent(product2);
        tx.commit();
    } finally {
        if (tx.isActive()) {
            tx.rollback();
        }
    }

    pm.close();

    pm = pmf.getPersistenceManager();

    tx = pm.currentTransaction();
    try {
        tx.begin();
        System.out.println();
        System.out.println("Fetch from store (before removal)");
        Supplier s = pm.getObjectById(Supplier.class, supplier.getId());
        System.out.println("supplier name: " + s.getName());
        System.out.println("# products: " + s.getProducts().size());
        Product p = pm.getObjectById(Product.class, product.getId());
        System.out.println("product name: " + p.getName());
        System.out.println("# suppliers: " + p.getSuppliers().size());
        Product p2 = pm.getObjectById(Product.class, product2.getId());
        System.out.println("product name: " + p2.getName());
        System.out.println("# suppliers: " + p2.getSuppliers().size());
        tx.commit();
    } finally {
        if (tx.isActive()) {
            tx.rollback();
        }
    }

    pm.close();

    Set<Product> updatedProducts = supplier.getProducts();
    updatedProducts.remove(product);
    Set<Supplier> updatedSuppliers = product.getSuppliers();
    updatedSuppliers.remove(supplier);

    System.out.println();
    System.out.println("Before persist (after removal)");
    System.out.println("supplier name: " + supplier.getName());
    System.out.println("# products: " + supplier.getProducts().size());
    System.out.println("product name: " + product.getName());
    System.out.println("# suppliers: " + product.getSuppliers().size());
    System.out.println("product name: " + product2.getName());
    System.out.println("# suppliers: " + product2.getSuppliers().size());

    pm = pmf.getPersistenceManager();

    tx = pm.currentTransaction();
    try {
        tx.begin();
        pm.makePersistent(supplier);
        pm.makePersistent(product);
        tx.commit();
    } finally {
        if (tx.isActive()) {
            tx.rollback();
        }
    }

    pm.close();

    System.out.println();
    System.out.println("After persist");
    System.out.println("supplier name: " + supplier.getName());
    System.out.println("# products: " + supplier.getProducts().size());
    System.out.println("product name: " + product.getName());
    System.out.println("# suppliers: " + product.getSuppliers().size());
    System.out.println("product name: " + product2.getName());
    System.out.println("# suppliers: " + product2.getSuppliers().size());

    pm = pmf.getPersistenceManager();

    tx = pm.currentTransaction();
    try {
        tx.begin();
        System.out.println();
        System.out.println("Fetch from store");
        Supplier s = pm.getObjectById(Supplier.class, supplier.getId());
        System.out.println("supplier name: " + s.getName());
        System.out.println("# products: " + s.getProducts().size());
        Product p = pm.getObjectById(Product.class, product.getId());
        System.out.println("product name: " + p.getName());
        System.out.println("# suppliers: " + p.getSuppliers().size());
        Product p2 = pm.getObjectById(Product.class, product2.getId());
        System.out.println("product name: " + p2.getName());
        System.out.println("# suppliers: " + p2.getSuppliers().size());
        tx.commit();
    } finally {
        if (tx.isActive()) {
            tx.rollback();
        }
    }

    pm.close();
}

http://www.datanucleus.org/products/datanucleus/jdo/orm/many_to_many.html

示例中使用的2种类型
@PersistenceCapable(detachable="true")
public class Supplier {

@PrimaryKey
@Persistent(valueStrategy=IdGeneratorStrategy.INCREMENT)
private long id;

@Persistent
private String name;

@Persistent(mappedBy="suppliers")
private Set<Product> products = new HashSet<Product>();

public long getId() {
    return id;
}

public void setId(long id) {
    this.id = id;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public Set<Product> getProducts() {
    return products;
}

public void setProducts(Set<Product> products) {
    this.products = products;
}

@Override
public boolean equals(Object o) {
    if(this.getId() == 0) {
        return false;
    }
    if(o instanceof Supplier) {
        Supplier other = (Supplier) o;
        return this.getId() == other.getId();
    }
    return false;
}

@Override
public int hashCode() {
    return Long.toString(this.getId()).hashCode();
}
}

@PersistenceCapable(detachable="true")
public class Product {


@PrimaryKey
@Persistent(valueStrategy=IdGeneratorStrategy.INCREMENT)
private long id;


@Persistent
private String name;

@Persistent
private double price;

@Persistent(table="PRODUCTS_SUPPLIERS")
@Join(column="PRODUCT_ID")
@Element(column="SUPPLIER_ID")
private Set<Supplier> suppliers = new HashSet<Supplier>();


public long getId() {
    return id;
}

public void setId(long id) {
    this.id = id;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public double getPrice() {
    return price;
}

public void setPrice(double price) {
    this.price = price;
}

public Set<Supplier> getSuppliers() {
    return suppliers;
}

public void setSuppliers(Set<Supplier> suppliers) {
    this.suppliers = suppliers;
}

@Override
public boolean equals(Object o) {
    if(getId() == 0) {
        return false;
    }
    if(o instanceof Product) {
        Product other = (Product) o;
        return this.getId() == other.getId();
    }
    return false;
}

@Override
public int hashCode() {
    return Long.toString(this.getId()).hashCode();
}

}

现在,在控制台中我得到了

Fetch from store (before removal)
supplier name: Supplier 1
# products: 2
product name: Product 1
# suppliers: 1
product name: Product 2
# suppliers: 1

Before persist (after removal)
supplier name: Supplier 1
# products: 1
product name: Product 1
# suppliers: 0
product name: Product 2
# suppliers: 1

After persist
supplier name: Supplier 1
# products: 1
product name: Product 1
# suppliers: 1
product name: Product 2
# suppliers: 1

Fetch from store
supplier name: Supplier 1
# products: 1
product name: Product 1
# suppliers: 1
product name: Product 2
# suppliers: 1

我希望像

这样的东西
Fetch from store (before removal)
supplier name: Supplier 1
# products: 2
product name: Product 1
# suppliers: 1
product name: Product 2
# suppliers: 1

Before persist (after removal)
supplier name: Supplier 1
# products: 1
product name: Product 1
# suppliers: 0
product name: Product 2
# suppliers: 1

After persist
supplier name: Supplier 1
# products: 1
product name: Product 1
# suppliers: 0
product name: Product 2
# suppliers: 1

Fetch from store
supplier name: Supplier 1
# products: 1
product name: Product 1
# suppliers: 0
product name: Product 2
# suppliers: 1

我的persistence.xml包括:

<property name="datanucleus.DetachAllOnCommit" value="true" />
<property name="datanucleus.attachSameDatastore" value="true" />
<property name="datanucleus.CopyOnAttach" value="false" />
<property name="datanucleus.cache.collections.lazy" value="true" />
<property name="datanucleus.manageRelationships" value="true" />
<property name="datanucleus.manageRelationshipsChecks" value="true" />

关于如何解决这个问题的任何想法?

1 个答案:

答案 0 :(得分:0)

我已经找到了解决问题的方法。这个问题似乎与我的持久性设置有关。我将设置更改为

<property name="datanucleus.DetachAllOnCommit" value="true" />
<property name="datanucleus.attachSameDatastore" value="true" />
<property name="datanucleus.CopyOnAttach" value="true" />
<property name="datanucleus.cache.collections.lazy" value="true" />
<property name="datanucleus.manageRelationships" value="false" />
<property name="datanucleus.manageRelationshipsChecks" value="false" />
<property name="datanucleus.persistenceByReachabilityAtCommit" value="false" />

因为我现在在我的实现中处理关系的两个方面。

此外,我实施了一个工厂来简化我的测试

public class Factory {

  private static final PersistenceManagerFactory pmf = get();

  private static PersistenceManagerFactory get() {
    return JDOHelper.getPersistenceManagerFactory("MyStore");
  }

  public static Product buildProduct(String name, double price) {
    Product product = new Product();
    product.setName(name);
    product.setPrice(price);
    save(product);
    return product;
  }

  public static Supplier buildSupplier(String name) {
    Supplier supplier = new Supplier();
    supplier.setName(name);
    save(supplier);
    return supplier;
  }

  public static void addRelation(final Supplier supplier, final Product product) {
    product.getSuppliers().add(supplier);
    supplier.getProducts().add(product);
    save(product, supplier);
  }

  public static void removeRelation(Supplier supplier, Product product) {
    product.getSuppliers().remove(supplier);
    supplier.getProducts().remove(product);
    save(product, supplier);
  }

  public static Product loadProduct(long id) {
    return (Product) load(Product.class, id);
  }

  public static Supplier loadSupplier(long id) {
    return (Supplier) load(Supplier.class, id);
  }

  private static void save(final Object... objects) {
    PersistenceManager pm = pmf.getPersistenceManager();
    Transaction tx = pm.currentTransaction();
    try {
      tx.begin();
      pm.makePersistentAll(objects);
      tx.commit();
    } finally {
      if (tx.isActive()) {
        tx.rollback();
      }
    }
    pm.close();
  }

  private static Object load(Class<?> clazz, long id) {
    final Object[] result = new Object[1];
    PersistenceManager pm = pmf.getPersistenceManager();
    Transaction tx = pm.currentTransaction();
    try {
      tx.begin();
      result[0] = pm.getObjectById(clazz, id);
      tx.commit();
    } finally {
      if (tx.isActive()) {
        tx.rollback();
      }
    }
    pm.close();
    if (result[0] == null) {
      throw new NoSuchElementException(clazz.getSimpleName() + " with id=" + id + " cannot be found");
    }
    return result[0];
  }
}

所以测试只是表示为

@Test
public void testFactoryApproach() {
    Product p1 = Factory.buildProduct("product1", 50);
    Product p2 = Factory.buildProduct("product2", 70);
    Supplier s1 = Factory.buildSupplier("Supplier1");

    System.out.println("Initial:");
    System.out.println(p1);
    System.out.println(p2);
    System.out.println(s1);
    System.out.println();

    Factory.addRelation(s1, p1);
    Factory.addRelation(s1, p2);

    System.out.println("Relations after add:");
    System.out.println(p1);
    System.out.println(p2);
    System.out.println(s1);
    System.out.println();

    Factory.removeRelation(s1, p1);

    System.out.println("Relations after remove:");
    System.out.println(p1);
    System.out.println(p2);
    System.out.println(s1);
    System.out.println();

    System.out.println("Relations after load:");
    System.out.println(Factory.loadProduct(p1.getId()));
    System.out.println(Factory.loadProduct(p2.getId()));
    System.out.println(Factory.loadSupplier(s1.getId()));
}

以下输出结果

Initial:
Product:11 (product1) - # suppliers = 0 []
Product:12 (product2) - # suppliers = 0 []
Supplier:11 (Supplier1) - # products = 0 []

Relations after add:
Product:11 (product1) - # suppliers = 1 [11:Supplier1]
Product:12 (product2) - # suppliers = 1 [11:Supplier1]
Supplier:11 (Supplier1) - # products = 2 [11:product1, 12:product2]

Relations after remove:
Product:11 (product1) - # suppliers = 0 []
Product:12 (product2) - # suppliers = 1 [11:Supplier1]
Supplier:11 (Supplier1) - # products = 1 [12:product2]

Relations after load:
Product:11 (product1) - # suppliers = 0 []
Product:12 (product2) - # suppliers = 1 [11:Supplier1]
Supplier:11 (Supplier1) - # products = 1 [12:product2]