2013-04-05 4 views
9

Je suis nouveau à la chose JPA ensemble, j'ai donc plusieurs questions sur la meilleure façon de gérer la fusion JPA et de persister.La meilleure façon de gérer la fusion JPA?

  1. J'ai un objet utilisateur qui devrait être mis à jour (certaines valeurs comme la date et le nom). Dois-je d'abord fusionner l'objet passé ou est-il plus sûr de trouver le nouvel objet?

    Actuellement mon code pour mettre à jour un utilisateur ressemble à ceci:

    public void updateUserName(User user, String name) { 
        // maybe first merge it? 
        user.setName(name); 
        user.setChangeDate(new Date()); 
        em.merge(user); 
    } 
    

    Comment puis-je être sûr que l'utilisateur n'a pas été manipulé avant que la méthode de mise à jour est appelée? Est-il plus sûr de faire quelque chose comme ceci:

    public void updateUserName(int userId, String name) { 
        User user = em.getReference(User.class, userId); 
        user.setName(name); 
        user.setChangeDate(new Date()); 
        em.merge(user); 
    } 
    

    Peut-être d'autres solutions? J'ai regardé plusieurs vidéos et vu de nombreux exemples, mais ils étaient tous différents et personne n'a expliqué quelle était la meilleure pratique.

  2. Quelle est la meilleure approche pour ajouter des enfants à des relations? Par exemple mon objet utilisateur a une connexion à plusieurs groupes. Dois-je appeler le gestionnaire JPA pour les utilisateurs et ajouter le nouveau groupe à la liste des groupes d'utilisateurs ou dois-je créer le groupe dans un gestionnaire de groupe avec persist et l'ajouter manuellement à mon objet utilisateur?

espère que quelqu'un a ici un indice;)

+0

Voir http://stackoverflow.com/questions/1069992/jpa-entitymanager-why-use-persist-over-merge – GKislin

Répondre

3

Question 1

  • La méthode merge doit être appelée sur une entité détachée.
  • La méthode merge renvoie l'objet fusionné attaché au entityManager.

Qu'est-ce que cela signifie?

Une entité est détachée dès que le entityManager que vous utilisez pour l'extraire est fermé. (c'est-à-dire la plupart du temps parce que vous l'avez récupéré lors d'une transaction précédente).

Dans votre deuxième exemple de code: l'utilisateur est attaché (parce que vous venez de le récupérer) et l'appel de la fusion est donc inutile. (BTW: ce n'est pas getReference mais find)

Dans votre premier échantillon: nous ne connaissons pas l'état de l'utilisateur (entité détachée ou non?). Si elle est détachée, il est logique d'appeler merge, mais attention à ce que merge ne modifie pas son objet passé en argument. Voici donc ma version de votre premier échantillon:

/** 
* @param user : a detached entity 
* @return : the attached updated entity 
**/ 
public User updateUserName(User user, String name) { 
    user.setName(name); 
    user.setChangeDate(new Date()); 
    return em.merge(user); 
} 

Question 2

Peut-être un exemple de code pour expliquer ce que vous entendez par gestionnaire JPA peut nous aider à comprendre vos préoccupations. Quoi qu'il en soit, je vais essayer de vous aider.

Si vous avez un utilisateur persistant et vous devez créer un nouveau groupe et l'associer à l'utilisateur persistant:

User user = em.find(User.class, userId); 
Group group = new Group(); 
... 
em.persist(group); 
user.addToGroups(group); 
group.addToUsers(user); //JPA won't update the other side of the relationship 
         //so you have to do it by hand OR being aware of that 

Si vous avez un utilisateur persistant et un groupe persistant et vous devez les associer :

User user = em.find(User.class, userId); 
Group group = em.find(Group.class, groupId); 
... 
user.addToGroups(group); 
group.addToUsers(user); 

considérations générales

Le meilleur les pratiques concernant tout cela dépendent vraiment de la gestion des transactions (et donc du cycle de vie de entityManager) par rapport au cycle de vie de vos objets.

La plupart du temps: un entityManager est un objet vivant très court. D'autre part vos objets métier peuvent vivre plus longtemps et vous devrez donc appeler la fusion (et attention au fait que la fusion ne modifie pas l'objet passé en argument !!!). Vous pouvez décider d'extraire et de modifier vos objets métier dans la même transaction (c'est-à-dire avec le même entityManager): cela signifie beaucoup plus d'accès à la base de données et cette stratégie doit généralement être associée à un cache de second niveau. Mais dans ce cas, vous n'aurez pas à appeler la fusion.

J'espère que cette aide.

+0

Merci pour votre réponse. En ce qui concerne ma question 1, n'est-ce pas le moyen le plus simple de définir mes champs comme date et nouveau nom dans ma méthode qui appelle le gestionnaire JPA et appelle simplement une méthode "updateUser (User user)" pour fusionner l'objet utilisateur et enregistrer toutes les modifications au lieu de créer des méthodes pour plusieurs cas d'utilisation de mise à jour? – ManuPanu

6
  1. Cela dépend de ce que vous voulez obtenir et de la quantité d'informations dont vous disposez sur l'origine de l'objet que vous essayez de fusionner. Tout d'abord, si vous appelez em.merge(user) dans la première ligne de votre méthode ou à la fin, cela n'a pas d'importance. Si vous utilisez JTA et CMT, votre entité sera mise à jour lorsque l'appel de méthode se termine. La seule différence est que si vous invoquez em.merge(user) avant de changer l'utilisateur, vous devez utiliser l'instance retournée au lieu de votre paramètre, donc soit il est:

    public void updateUserName(User user, String name) { 
        User managedUser = em.merge(user); 
        managedUser.setChangeDate(new Date()); 
        // no need of additional em.merge(-) here. 
        // JTA automatically commits the transaction for this business method. 
    } 
    

    ou

    public void updateUserName(User user, String name) { 
        user.setChangeDate(new Date()); 
        em.merge(user); 
        // JTA automatically commits the transaction for this business method. 
    } 
    

    Parlons maintenant de l'entité mise à jour.
    Si vous souhaitez simplement mettre à jour des champs bien définis dans votre entité, utilisez la seconde approche car elle est plus sûre. Vous ne pouvez pas être sûr qu'un client de votre méthode n'a pas modifié d'autres champs de votre entité. Par conséquent, em.merge(-) les mettra également à jour, ce qui pourrait ne pas être ce que vous vouliez réaliser. D'autre part, si vous voulez accepter toutes les modifications effectuées par l'utilisateur et simplement remplacer/ajouter des propriétés comme changeDate dans votre exemple, la première approche est également correcte (fusionner l'entité entière passée à la méthode métier). dépend vraiment de votre cas d'utilisation.

  2. Je suppose que cela dépend de vos paramètres en cascade. Si vous voulez automatiquement persister/fusionner tous Groups chaque fois que l'entité User est modifiée - il est sûr de l'ajouter à la collection de l'utilisateur (quelque chose comme User#addGroup(Group g) { groups.add(g)}.) Si vous ne voulez pas de cascade, vous pouvez toujours créer vos propres méthodes qui se propageront De l'autre côté de la relation, il peut s'agir de: User#addGroup(Group g) qui appelle automatiquement g.addUser(this);.

Questions connexes