在Google App Engine上使用JDO更新“嵌套”对象

时间:2009-09-27 01:32:18

标签: java google-app-engine orm jdo

我无法找到使用Google App Engine更新“嵌套”数据的正确方法 和JDO。我有一个RecipeJDO和一个IngredientJDO

我希望能够使用新的成分列表完全替换给定配方实例中的成分。然后,当(重新)持续该配方时,任何以前附加的成分将完全从数据存储中删除,并且新的成分将被保留并与配方相关联。

类似的东西:

  // retrieve from GAE datastore
  RecipeJDO recipe = getRecipeById();    

  // fetch new ingredients from the user
  List<IngredientJDO> newIngredients = getNewIngredients();
  recipe.setIngredients(newIngredients);

  // update the recipe w/ new ingredients
  saveUpdatedRecipe(recipe);

当我直接更新(分离)配方对象(从数据存储区返回)时,这可以正常工作。但是,如果我复制一个RecipeJDO,然后进行上述更新,它最终会附加新的成分,然后在从数据存储区重新获取配方时将其与旧成分一起返回。 (为什么要复制副本?我在前端使用GWT,所以我将JDO对象复制到DTO,用户在前端编辑它们,然后将它们发送到后端以更新数据存储。)

为什么我使用手工创建的对象(设置所有字段,包括id)与对PersistenceManager返回的实例进行操作会得到不同的结果?明显 JDO的字节码增强以某种方式涉及。

我最好只是在坚持更新之前明确删除旧成分 配方

(附带问题 - 其他人是否对ORM感到沮丧并希望我们可以回到原来的旧RDBMS?: - )

3 个答案:

答案 0 :(得分:4)

简短回答。将RecipeJDO.setIngredients()更改为:

public void setIngredients(List<IngredientJDO> ingredients) {
  this.ingredients.clear();
  this.ingredients.addAll(ingredients);
}

当您获取RecipeJDO时,ingredients列表不是真正的ArrayList,它是一个动态代理,用于处理所包含元素的持久性。你不应该替换它。

当持久性管理器处于打开状态时,您可以遍历ingredients列表,添加项目或删除项目,并且在持久性管理器关闭时(或者提交事务,如果您是在交易中)。以下是在没有交易的情况下进行更新的方法:

public void updateRecipe(String id, List<IngredientDTO> newIngredients) {
  List<IngredientJDO> ingredients = convertIngredientDtosToJdos(newIngredients);
  PersistenceManager pm = PMF.get().getPersistenceManager();
  try {
    RecipeJDO recipe = pm.getObjectById(RecipeJDO.class, id);
    recipe.setIngredients(ingredients);
  } finally {
    pm.close();
  }
}

如果您从不修改IngredientJDO个对象(只替换它们并读取它们),您可能希望将它们设为Serializable个对象而不是JDO对象。如果这样做,您可以在GWT RPC代码中重用Ingredient类。

顺便说一句,即使Recipe不是JDO对象,您也希望使用setIngredients()方法制作副本,否则有人可以这样做:

List<IngredientJDO> ingredients = new ArrayList<IngredientJDO>;
// add items to ingredients
recipe.setIngredients(ingredients);
ingredients.clear(); // Woops! Modifies Recipe!

答案 1 :(得分:0)

我面临同样的问题! 我想通过调用makePersistent()并分配一个现有的id / key来更新现有实体!除嵌套对象外,更新工作正常!嵌套对象是否附加到旧对象而不是被替换?我不知道这是否是预期的行为,或者这是一个错误?我希望覆盖与插入新实体具有相同的效果!

如何首先删除旧实体并在同一事务中保留新实体?这有用吗?我尝试了这个,但它导致完全删除了实体?!我不知道为什么(即使我在删除后尝试直接刷新)!

答案 2 :(得分:0)

@NamshubWriter,不确定你是否会收到这篇文章...关于你的评论,

  

(如果您使用Stripes和JSP,则可以避免配方和成分的GWT RPC和GWT模型表示)

使用Stripes和JSP,但我遇到了同样的问题。当用户提交表单时,Stripes从头开始实例化我的实体对象,因此JDO完全不知道它们。当我在根对象上调用PersistenceManager.makePersistent时,前一版本被正确覆盖 - 除了一个例外,其对象追加 List&lt; child&gt; 以前的版本。

如果您可以建议任何解决方案(比手动复制对象字段更好),我将不胜感激。

(看作Stripes是如此可插拔,我想知道我是否可以覆盖它实例化实体对象的方式......)