2009-09-27 4 views
2

Je n'arrive pas à trouver la bonne façon de mettre à jour les données "imbriquées" à l'aide de Google App Engine et JDO. J'ai un RecipeJDO et un IngredientJDO.Mise à jour des objets "imbriqués" avec JDO sur Google App Engine

Je veux être complètement remplacer les ingrédients dans une instance de recette donnée avec une nouvelle liste d'ingrédients. Ensuite, lorsque cette recette est (re) conservée, tous les ingrédients précédemment attachés seront totalement supprimés du magasin de données, et les nouveaux seront conservés et associés à la recette.

Quelque chose comme:

// 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); 

Cela fonctionne bien quand je mets à jour (détaché) recette objets directement, retournée par le magasin de données. Toutefois, si je recopie un RecipeJDO, puis faire les mises à jour mentionnées ci-dessus, il finit par ajouter les nouveaux ingrédients, qui sont ensuite retournés avec les anciens ingrédients lorsque la recette est ensuite récupérée à partir du magasin de données. (Pourquoi s'embêter avec la copie?) J'utilise GWT sur le frontal, donc je copie les objets JDO vers les DTO, l'utilisateur les édite sur le frontal, puis ils sont envoyés au backend pour mettre à jour le banque de données.)

Pourquoi est-ce que j'obtiens des résultats différents avec les objets que je crée manuellement (en définissant tous les champs, y compris l'ID) vs en opérant sur les instances renvoyées par PersistenceManager? Évidemment L'amélioration de bytecode de JDO est impliquée en quelque sorte. Est-il préférable de supprimer explicitement les vieux ingrédients avant de continuer la recette mise à jour de ?

(question- côté que quelqu'un d'autre se sentent frustrés avec ORM et que vous souhaitez que nous pourrions revenir à bon vieux SGBDR? :-)

Répondre

4

Réponse courte. Changer RecipeJDO.setIngredients() à ceci:

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

Lorsque vous allez chercher la RecipeJDO, la liste ingredients est pas un vrai ArrayList, il est un proxy dynamique qui gère la persistance des éléments contenus. Vous ne devriez pas le remplacer. Lorsque le gestionnaire de persistance est ouvert, vous pouvez parcourir la liste ingredients, ajouter des éléments ou supprimer des éléments, et les modifications seront conservées lorsque le gestionnaire de persistance est fermé (ou que la transaction est validée si vous êtes dans une transaction). Voici comment vous feriez la mise à jour sans une transaction:

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

Si vous ne modifiez les objets IngredientJDO (seulement les remplacer et de les lire), vous pouvez les faire Serializable objets au lieu des objets JDO. Si vous faites cela, vous pourrez peut-être réutiliser la classe Ingredient dans votre code RPC GWT.

Soit dit en passant, même si Recipe était pas un objet JDO, vous voulez faire une copie dans la méthode setIngredients(), sinon quelqu'un pourrait le faire:

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

Désolé, cela ne résout pas le problème. Je ne l'ai pas très bien articulé. Quand j'obtiens les recettes (non-JDO) de GWT, je crée de nouveaux objets RecipeJDO (j'appelle littéralement "new RecipeJDO()"), copiez tous les champs, et ensuite faites pm.makePersistent() dessus. C'est la création via "new" qui cause le problème. Si je prends à la place le RecipeJDO à partir du datastore, puis que je copie les champs, tout va bien. Je suppose que la solution est de récupérer l'ancien RecipeJDO à partir du magasin de données, de copier les champs dans cet objet, puis de mettre à jour cet objet. SQL a l'air mieux tout le temps. ;-) –

+0

Si vous mettez à jour une recette existante, ne créez pas un nouvel objet RecipeJDO. Créer un nouvel objet ReceipeJDO et appeler makePersistent(), c'est comme faire un SQL INSERT. Les données étaient déjà insérées lors de la création de la recette. Après cela, vous devriez le mettre à jour. – NamshubWriter

+0

Oui, mais je mets l'identifiant de RecipeJDO à l'identifiant d'une recette existante dans le datastore, donc cela devrait fonctionner comme une mise à jour. Et c'est le cas, sauf que les (nouveaux) ingrédients sont ajoutés à la liste des ingrédients existants, plutôt que de les remplacer. Il semble que je devrais peut-être récupérer le RecipeJDO à partir de PersistenceManager, mettre à jour ses champs, supprimer tous les ingrédients existants (en utilisant sa liste d'origine), puis ajouter les nouveaux ingrédients, puis recommencer. Bien que cela fasse probablement ce dont j'ai besoin, il me semble qu'il y a énormément de travail qui en quelque sorte nie l'utilité d'un ORM en premier lieu. –

0

Je suis confronté au même problème! Je voudrais mettre à jour une entité existante en appelant makePersistent() et en assignant un identifiant/clé existant! la mise à jour fonctionne bien, sauf pour les objets imbriqués!Les objets imbriqués sont ajoutés aux anciens au lieu d'être remplacés? Je ne sais pas si c'est le comportement prévu ou si c'est un bug? Je prévois que l'écrasement aura le même effet que l'insertion d'une nouvelle entité!

Que diriez-vous d'abord de supprimer l'ancienne entité et de conserver la nouvelle dans la même transaction? Est-ce que ça marche? J'ai essayé cela mais cela a entraîné la suppression complète de l'entité ?! Je ne sais pas pourquoi (même si j'ai essayé de rincer directement après la suppression)!

0

@NamshubWriter, ne sais pas si vous attrapez ce post ... au sujet de votre commentaire,

(si vous avez utilisé Stripes et JSP, vous pouvez éviter les représentations du modèle GWT RPC et GWT de recettes et ingrédients)

I am utilisant Stripes et JSP, mais je face au même problème. Lorsque l'utilisateur remet le formulaire, Stripes instancie mes objets d'entité à partir de rien, et JDO les ignore complètement. Quand j'appelle PersistenceManager.makePersistent sur l'objet racine, la version précédente est remplacée correctement - à une exception près, ses objets enfants sont joint en annexe à laListe <enfant> de la version précédente.

Si vous pouviez suggérer une solution (mieux que de copier manuellement les champs d'objet) j'apprécierais grandement.

(car Stripes est si connectable, je me demande si je peux passer outre la façon dont il instancie les objets entité ...)