2009-02-25 6 views
1

Supposons que j'ai un objet Utilisateur mappé avec un user_id comme clé primaire et une colonne de courrier avec une contrainte unique. Je souhaite que mon programme enregistre un objet Utilisateur dans la table des utilisateurs uniquement s'il n'existe pas.Insertion d'un objet sur la base de données en mode hibernation, uniquement si elle n'existe pas

je peux faire la chose suivante dans ma fonction d'insertion:

  1. begin transaction
  2. requête pour un utilisateur avec le courrier donné
  3. si elle n'existe pas, créez un nouvel utilisateur et enregistrer à l'aide de la session
  4. validée ou annulée en cas d'échec

il y a deux problèmes:

  1. Il est coûteux d'effectuer deux requêtes. Si j'utilisais du SQL simple, je pourrais utiliser la syntaxe "INSERT IGNORE", mais dans mon cas, je veux utiliser la méthode de sauvegarde hibernate, et je veux aussi maintenir l'objet User à la fin. Y a-t-il une solution pour cela?
  2. Concurrence. Est-il possible que deux instances de mon application s'exécutent en même temps. Les deux utiliseront la fonction "addUser" avec le même paramètre "mail". Le premier passera par la phase 2 et ne trouvera pas un utilisateur avec ce courrier. Le second passera également par la phase 2 sans trouver d'utilisateur car la première instance n'a toujours pas sauvegardé son utilisateur et validé. Ensuite, la première instance enregistrera et validera l'utilisateur, et ensuite la seconde instance enregistrera et validera l'utilisateur, qui a le même courrier que le courrier de la première instance. Bien sûr, ça va échouer, à cause de la contrainte. Ai-je raté quelque chose?

Si je suis en ce que dois-je faire:

  1. espoir que cela ne se produira pas.
  2. Verrouillez la table des utilisateurs pour lecture et écriture au début de la transaction.
  3. Ajouter try ... sauf pour la fonction, à l'exception d'une exception de contrainte unique, et si l'exception est levée, sélectionnera simplement l'utilisateur de la base de données en fonction de son courrier.
  4. Autres solutions ...

Répondre

1

Vous avez toujours à peu près le potentiel de ce genre de situations, sauf si vous introduisez tant le verrouillage de votre système devient inutilisable. Si vous ne savez pas qu'il s'agit d'un problème, concentrez-vous simplement sur la gestion de la violation de contrainte unique.

0

vous devriez simplement vous concentrer sur la manipulation de la violation de contrainte unique gracieusement.

Vous pouvez le faire en attrapant l'exception ConstraintViolationException qui sera levée.Si vous voulez être plus grainular, si peut-être il y a plusieurs viilations de Contraint qui peuvent se produire, vous pouvez faire quelque chose comme ceci:

... 
} catch (ConstraintViolationException e) { 
    if (e.getConstraintName() != null && e.getConstraintName().toLowerCase().equals("the_name_of_your_contraint")) { 
     // do something, like add an error to be displayed on the UI 
    } 
} 
+3

Hm, je considère cela comme une mauvaise pratique. Les exceptions ne sont pas destinées à contrôler le flux. Voir "Java efficace" :-) – Fortega

+1

@Fortega, je suis d'accord. Je préfère prendre le coup de performance et déterminer si l'utilisateur existe avant l'insertion. –

0

Pour l'instant la façon unique que je peux faire cela fonctionne:

<sql-insert check="none" callable="true"> 
      {call saveOrUpdate (?, ?, ?, ?, ?)} 
</sql-insert> 

http://forum.springsource.org/showthread.php?65265-Hibernate-insert-problem

Autres options de je ne peux pas faire cela fonctionne:

peut-être u peut faire un "insert où ne pas exister" avec l'annotation @SQLInsert Hibernate

l'option suivante est uniquement pour les instances d'édition:

autre option consiste à utiliser « optimist verrouillage avec colonne de version » et la gestion des exceptions dans le cas d'une exception de la concurrence.

lien: http://docs.jboss.org/hibernate/core/3.3/reference/en/html/transactions.html#transactions-optimistic

Questions connexes