8
votes

Java: Engagez-vous VS Rollback vs rien lorsque la sémantique est inchangée?

OK, je connais la différence entre commetter et retourner et ce que ces opérations sont censées faire. Cependant, je ne suis pas certain de quoi faire dans les cas où je peux réaliser le même comportement lors de l'utilisation de COMMIS (), Rollback () et / ou ne rien faire.

Par exemple, disons que j'ai le code suivant qui exécute une requête sans écrire à dB: Je travaille sur une application qui communique avec la base de données SQLite. P> xxx pré>

ou encore plus intéressant, considérez le code suivant, qui supprime une seule ligne: P>

try {
  if (deleteById(2))
    // a) delete successful (1 row deleted)
  else
    // b) delete unsuccessful (0 row deleted, no errors)
} catch (SQLException e) {
  // c) delete failed (because of an error (possibly due to constraint violation in DB))
}
  • commit li>
  • Rollback LI>
  • ne rien faire li> ul>

    existe-t-il des lignes directrices ou des avantages de performance du choix d'une opération particulière? Quelle est la bonne façon? P>

    Remarque: supposons que l'auto-commit est désactivé. P> p>


7 commentaires

Si la base de données a une transaction ouverte, elle sera probablement plus rapide de s'engager. Pour une restauration, la DB doit vérifier le journal des transactions et rétablir les modifications apportées. Même si ce n'est "aucun changement", le chèque se produira toujours.


Rollback permettrait également d'annuler les autres mises à jour effectuées dans la même transaction. Où vous en avez un seul, cela ne fait pas une différence (sauf que JNK décrit), mais si vous en ajoutez plus tard et oubliez de le gérer, vous risquez de vous faire piquer. Une bonne habitude d'entrer dans.


@JNK dépend légèrement de la SGBDM - il existe également un conflit potentiel avec plutôt que des déclencheurs - mais cela serait quelque peu compliqué.


@ADEWREW - YEP a accepté, dépendant de la SGBDM. Je ne l'ai pas posté comme une réponse parce que je ne connais pas trop bien JDBC non plus que :)


@Paulmedcraft: Vous dites donc essentiellement que je devrais toujours vous engager ou retourner, comme que rien ne peut entraîner certaines conséquences indésirables au cas où une mise à jour réussie est suivie d'une autre opération après laquelle une annulation a lieu (annulation ainsi le premier), droit? Mais si je sais que la mise à jour a échoué (voir les cas à l'intérieur du bloc de capture)? Ne fait rien de résultats dans une performance boost?


Habituellement, lorsque vous effectuez plusieurs mises à jour dans une méthode, ou une chaîne de méthodes, vous souhaitez qu'ils soient traités comme une unité atomique. Donc, si une partie échoue, vous voulez que tous la restauration, votre dB ne reste pas dans un état incohérent (bien que dans l'opération, vous pourriez disposer d'IDS sorties d'une séquence dans un TXN séparé qui ne roule jamais). Si vous entrez dans l'habitude de rouler toujours en cas d'erreur, vous êtes moins susceptible d'oublier de le faire si vous élargissez la portée de la méthode.


par exemple. Dites lorsque vous faites vos lignes de suppression au-dessus, vous avez besoin d'insérer des informations d'audit dans une table séparée. Si cette insertion d'audit échoue, mais l'ensemble TXN ne le retourne pas, vous aurez supprimé les lignes mais aucun enregistrement correspondant dans la table d'audit.


5 Réponses :


2
votes

S'il s'agit simplement d'une sélection, je n'ouvrirais pas une transaction et, par conséquent, il n'y a rien à faire en tout cas. Vous savez probablement déjà qu'il y a une mise à jour / insertion depuis que vous avez déjà passé les paramètres. Le cas où vous avez l'intention de faire une manipulation est plus intéressant. Le cas où il supprime est clair que vous souhaitez commettre; S'il y a une exception, vous devez retourner pour maintenir la DB cohérente depuis quelque chose qui a échoué et que vous ne pouvez pas faire beaucoup. Si la suppression a échoué parce qu'il n'y avait rien à supprimer, je vous engageons pour 3 raisons:

  • sémantiquement, il semble plus correct car l'opération était techniquement réussie et effectuée comme spécifié.

  • C'est plus de preuve future, donc si quelqu'un ajoute plus de code à la transaction, ils ne seront pas surpris par le retour de l'arrière car une suppression n'a rien fait (ils s'attendaient à une exception que la transaction est roulée)

  • Quand il y a une opération à faire, commett, c'est plus rapide, mais dans ce cas je Ne pense pas que ça compte.


3 commentaires

+1, mais: (1) "Si c'est juste une sélection, je n'oublierais pas une transaction" dépend de la SGBD, car dans certains, vous êtes toujours dans une trasaction; et (2) je suis d'accord avec vous que vous devriez faire revenir sur l'affaire à l'exception, mais c'est un flexible de dire «pas de discussion là-bas», puisque l'OP spécifiquement posé à propos de cette affaire, après avoir indiqué qu'un retour sur le dos et Un commit serait sémantiquement équivalent.


@ruakh (1) Je suis tout à fait d'accord. Pour (2) j'ai édité la réponse à prendre dans le commentaire


JDBC commence toujours une transaction (qui est soit une autocommande, soit la commission d'être commise / rollowback manuellement). Il n'y a aucune option pour ne pas démarrer une transaction. Certaines base de données ont une option de connexion «Readonly», ce qui signifie une manipulation des transactions moins chères pour cette connexion.



1
votes

Toute application non triviale aura des opérations nécessitant de multiples déclarations SQL à compléter. Tout échec sur la première instruction SQL et avant que la dernière instruction SQL entraînera une incohérence des données.

Les transactions sont conçues pour effectuer des opérations à plusieurs inscriptions en tant qu'atomic que les opérations de déclaration unique que vous utilisez actuellement.


0 commentaires

1
votes

Ce que vous dites dépend du code appelé, renvoie-t-il un drapeau à tester contre, ou exclusivement-il exclusivement des exceptions si quelque chose ne va pas?

API jette des exceptions mais renvoie également un booléen (vrai | faux): strong> p>

Cette situation se produit beaucoup, et il est difficile pour le code d'appel de gérer les deux conditions , comme vous l'avez souligné dans votre op. La seule chose que vous pouvez faire dans cette situation est la suivante: p> xxx pré>

API gagne exclusivement des exceptions (aucune valeur de retour) forte>: p>

Aucun problème. Nous pouvons supposer que si aucune exception n'a été prise, l'opération terminée sans erreur et nous pouvons ajouter la restauration () / commit () dans le bloc Type / Catch. P>

$queryStatus = true;

if (!deleteById(2)) {
    $queryStatus = false;
} 

if (!updateById(7)) {
    $queryStatus = false;
} 

...

if (true === $queryStatus) {
    commit();
} else {
    rollback();
}


0 commentaires

1
votes

Je me suis demandé exactement la même question. À la fin, je suis allé chercher une solution où j'ai toujours commis des transactions réussies et toujours des transactions non réussies non réussies considère ayant des effets. Ce lot de code simplifié simplifié et le rendit plus clair et plus facile à lire.

Il n'a pas eu de problèmes de performance majeurs dans l'application que j'ai travaillé dans lesquels utilisé NHibernate + SQLite sur .NET. Votre miliolier peut varier.


0 commentaires

1
votes

Comme d'autres ont déclaré dans leurs réponses, ce n'est pas une question de performance (pour les cas équivalents que vous avez décrits), que je crois est négligeable, mais une question de maintenabilité, et cela est toujours aussi important!

Pour Votre code doit être joliment maintenu, je vous suggère (peu importe quoi) de toujours commettre de toujours en bas de votre Essayez Block et de toujours fermer votre connexion dans votre enfin bloc. Dans le bloc enfin , vous devez également rétablir s'il y a des transactions non engagées (ce qui signifie que vous n'avez pas atteint le commit à la fin du essayer bloc).

Cet exemple montre ce que je crois que c'est la meilleure pratique (merveillabilité-sage): xxx


1 commentaires

Joli. Bien que je laisse même l'exception à la bulle pour ne pas perdre aucune information d'erreur utile telle que le message d'erreur sur le chemin, plutôt que de simplement retourner de faux.