Quelqu'un peut-il expliquer à quoi servent les paramètres d' isolation et de propagation dans l' @Transactional
annotation via un exemple réel?
Fondamentalement, quand et pourquoi je devrais choisir de changer leurs valeurs par défaut.
Quelqu'un peut-il expliquer à quoi servent les paramètres d' isolation et de propagation dans l' @Transactional
annotation via un exemple réel?
Fondamentalement, quand et pourquoi je devrais choisir de changer leurs valeurs par défaut.
Bonne question, mais pas insignifiante.
Définit la relation entre les transactions. Options courantes:
Required
: Le code s'exécutera toujours dans une transaction. Crée une nouvelle transaction ou en réutilise une si disponible.Requires_new
: Le code s'exécutera toujours dans une nouvelle transaction. Suspend la transaction en cours si elle existe.Définit le contrat de données entre les transactions.
Read Uncommitted
: Permet des lectures sales.Read Committed
: Ne permet pas les lectures sales.Repeatable Read
: Si une ligne est lue deux fois dans la même transaction, le résultat sera toujours le même.Serializable
: Effectue toutes les transactions dans une séquence.Les différents niveaux ont des caractéristiques de performances différentes dans une application multithread. Je pense que si vous comprenez le dirty reads
concept, vous pourrez sélectionner une bonne option.
Exemple de cas où une lecture incorrecte peut se produire:
thread 1 thread 2
| |
write(x) |
| |
| read(x)
| |
rollback |
v v
value (x) is now dirty (incorrect)
Ainsi, une valeur par défaut saine (si cela peut être revendiqué) pourrait être Read Committed
, qui vous permet uniquement de lire des valeurs qui ont déjà été validées par d'autres transactions en cours, en combinaison avec un niveau de propagation de Required
. Ensuite, vous pouvez travailler à partir de là si votre application a d'autres besoins.
Un exemple pratique où une nouvelle transaction sera toujours créée lors de l'entrée dans la provideService
routine et terminée lors de la sortie:
public class FooService {
private Repository repo1;
private Repository repo2;
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void provideService() {
repo1.retrieveFoo();
repo2.retrieveFoo();
}
}
Si nous avions utilisé à la place Required
, la transaction resterait ouverte si la transaction était déjà ouverte lors de l'entrée dans la routine. Notez également que le résultat d'un rollback
pourrait être différent car plusieurs exécutions pourraient participer à la même transaction.
Nous pouvons facilement vérifier le comportement avec un test et voir comment les résultats diffèrent avec les niveaux de propagation:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:/fooService.xml")
public class FooServiceTests {
private @Autowired TransactionManager transactionManager;
private @Autowired FooService fooService;
@Test
public void testProvideService() {
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
fooService.provideService();
transactionManager.rollback(status);
// assert repository values are unchanged ...
}
Avec un niveau de propagation de
Requires new
: nous nous attendions à ce qu'il fooService.provideService()
n'ait PAS été annulé car il a créé sa propre sous-transaction.
Required
: nous nous attendrions à ce que tout soit annulé et que le magasin de sauvegarde soit inchangé.
sessionFactory.getCurrentTransaction()
ajout, il n'est plus nécessaire d'exécuterHibernateTemplate
pour gérer les transactions. Je l'ai supprimé :)PROPAGATION_REQUIRED = 0 ; Si DataSourceTransactionObject T1 est déjà démarré pour la méthode M1.Si un autre objet Transaction M2 est requis, aucun nouvel objet Transaction n'est créé. Le même objet T1 est utilisé pour M2
PROPAGATION_MANDATORY = 2 ; doit s'exécuter dans une transaction. Si aucune transaction existante n'est en cours, une exception sera levée
PROPAGATION_REQUIRES_NEW = 3 ; Si DataSourceTransactionObject T1 est déjà démarré pour la méthode M1 et qu'il est en cours (exécution de la méthode M1) .Si une autre méthode M2 commence à s'exécuter, T1 est suspendu pendant la durée de la méthode M2 avec un nouveau DataSourceTransactionObject T2 pour M2.M2 exécuté dans son propre contexte de transaction
PROPAGATION_NOT_SUPPORTED = 4 ; Si DataSourceTransactionObject T1 est déjà démarré pour la méthode M1.Si une autre méthode M2 est exécutée simultanément, alors M2 ne doit pas s'exécuter dans le contexte de la transaction. T1 est suspendu jusqu'à ce que M2 soit terminé.
PROPAGATION_NEVER = 5 ; Aucune des méthodes ne s'exécute dans le contexte de transaction.
Un niveau d'isolement: il s'agit de savoir dans quelle mesure une transaction peut être affectée par les activités d'autres transactions simultanées. Il prend en charge la cohérence en laissant les données sur plusieurs tables dans un état cohérent. Il s'agit de verrouiller des lignes et / ou des tables dans une base de données.
Le problème des transactions multiples
Scénario 1 .Si la transaction T1 lit les données de la table A1 qui ont été écrites par une autre transaction simultanée T2.Si T2 est annulé, les données obtenues par T1 ne sont pas valides.Eg a = 2 sont les données d'origine .Si T1 lit a = 1 qui a été écrit par T2.Si T2 rollback alors a = 1 sera rollback à a = 2 dans DB.Mais, maintenant, T1 a a = 1 mais dans la table DB il est changé en a = 2.
Scénario2 .Si la transaction T1 lit les données de la table A1.Si une autre transaction simultanée (T2) met à jour les données de la table A1.Ensuite, les données que T1 a lues sont différentes de la table A1.Parce que T2 a mis à jour les données de la table A1.Eg si T1 lire a = 1 et T2 mis à jour a = 2. Puis a! = b.
Scénario 3 .Si la transaction T1 lit les données de la table A1 avec un certain nombre de lignes. Si une autre transaction simultanée (T2) insère plus de lignes dans la table A1.Le nombre de lignes lues par T1 est différent des lignes de la table A1
Le scénario 1 est appelé lectures sales.
Le scénario 2 est appelé lectures non répétables.
Le scénario 3 est appelé lectures fantômes.
Ainsi, le niveau d'isolement est l'étendue à laquelle le scénario 1, le scénario 2, le scénario 3 peuvent être empêchés. Vous pouvez obtenir un niveau d'isolement complet en implémentant le verrouillage, ce qui empêche les lectures et les écritures simultanées de se produire, mais cela affecte les performances. Le niveau d'isolement dépend de l'application à l'application de l'isolement requis.
ISOLATION_READ_UNCOMMITTED : Permet de lire les modifications qui n'ont pas encore été validées. Il souffre du scénario 1, du scénario 2, du scénario 3
ISOLATION_READ_COMMITTED : autorise les lectures à partir de transactions simultanées qui ont été validées. Il peut souffrir des scénarios 2 et 3. Parce que d'autres transactions peuvent mettre à jour les données.
ISOLATION_REPEATABLE_READ : plusieurs lectures du même champ produiront les mêmes résultats jusqu'à ce qu'il soit modifié par lui-même.Il peut souffrir du scénario 3.Parce que d'autres transactions peuvent insérer les données
ISOLATION_SERIALIZABLE : le scénario 1, le scénario 2, le scénario 3 ne se produisent jamais. Il s'agit d'une isolation complète. Il implique un verrouillage complet. Il affecte les performances en raison du verrouillage.
Vous pouvez tester en utilisant
Vous pouvez déboguer et voir le résultat avec différentes valeurs d'isolement et de propagation.
la source
Une explication suffisante de chaque paramètre est donnée par d'autres réponses; Cependant, vous avez demandé un exemple concret, voici celui qui clarifie le but des différentes options de propagation :
Supposons que vous soyez responsable de la mise en œuvre d'un service d'inscription dans lequel un e-mail de confirmation est envoyé à l'utilisateur. Vous obtenez deux objets de service, l'un pour l' inscription de l'utilisateur et l'autre pour l' envoi d' e-mails, ce dernier étant appelé dans le premier. Par exemple quelque chose comme ça:Vous avez peut-être remarqué que le deuxième service est de type propagation OBLIGATOIRE_NEW et en plus il y a de fortes chances qu'il lève une exception (serveur SMTP arrêté, e-mail invalide ou autres raisons) .Vous ne voulez probablement pas que l'ensemble du processus soit annulé, comme supprimer les informations utilisateur de la base de données ou d'autres choses; par conséquent, vous appelez le deuxième service dans une transaction distincte.
Revenons à notre exemple, cette fois, vous êtes préoccupé par la sécurité de la base de données, vous définissez donc vos classes DAO de cette façon:Cela signifie que chaque fois qu'un objet DAO, et donc un accès potentiel à db, est créé, nous devons nous assurer que l'appel a été effectué à l'intérieur de l'un de nos services, ce qui implique qu'une transaction en direct doit exister; sinon, une exception se produit. Par conséquent, la propagation est de type OBLIGATOIRE .
la source
Le niveau d'isolement définit la manière dont les modifications apportées à certains référentiels de données par une transaction affectent d'autres transactions simultanées simultanées, ainsi que la manière et le moment où ces données modifiées deviennent disponibles pour d'autres transactions. Lorsque nous définissons une transaction à l'aide du framework Spring, nous pouvons également configurer à quel niveau d'isolement cette même transaction sera exécutée.
Le niveau d'isolement READ_UNCOMMITTED indique qu'une transaction peut lire des données qui ne sont pas encore validées par d'autres transactions.
Le niveau d'isolement READ_COMMITTED indique qu'une transaction ne peut pas lire des données qui ne sont pas encore validées par d'autres transactions.
Le niveau d'isolement REPEATABLE_READ indique que si une transaction lit plusieurs fois un enregistrement de la base de données, le résultat de toutes ces opérations de lecture doit toujours être le même.
Le niveau d'isolement SERIALISABLE est le plus restrictif de tous les niveaux d'isolement. Les transactions sont exécutées avec verrouillage à tous les niveaux (verrouillage en lecture, en plage et en écriture) de sorte qu'elles apparaissent comme si elles avaient été exécutées de manière sérialisée.
La propagation est la capacité de décider comment les méthodes commerciales doivent être encapsulées dans les transactions logiques ou physiques.
Le comportement Spring REQUIRED signifie que la même transaction sera utilisée s'il existe une transaction déjà ouverte dans le contexte d'exécution actuel de la méthode du bean.
Le comportement REQUIRES_NEW signifie qu'une nouvelle transaction physique sera toujours créée par le conteneur.
Le comportement NESTED oblige les transactions Spring imbriquées à utiliser la même transaction physique mais définit des points de sauvegarde entre les invocations imbriquées afin que les transactions internes puissent également être annulées indépendamment des transactions externes.
Le comportement OBLIGATOIRE indique qu'une transaction ouverte existante doit déjà exister. Sinon, une exception sera levée par le conteneur.
Le comportement NEVER indique qu'une transaction ouverte existante ne doit pas déjà exister. Si une transaction existe, une exception sera levée par le conteneur.
Le comportement NOT_SUPPORTED s'exécutera en dehors de la portée de toute transaction. Si une transaction ouverte existe déjà, elle sera suspendue.
Le comportement SUPPORTS s'exécutera dans le cadre d'une transaction si une transaction ouverte existe déjà. S'il n'y a pas de transaction déjà ouverte, la méthode s'exécutera de toute façon mais de manière non transactionnelle.
la source
Une transaction représente une unité de travail avec une base de données.
Dans l'
TransactionDefinition
interface Spring qui définit les propriétés de transaction conformes à Spring.@Transactional
annotation décrit les attributs de transaction sur une méthode ou une classe.Perception du verrouillage: le niveau d'isolement détermine la durée de maintien des verrous.
Lire la perception: les 3 types de problèmes majeurs suivants se produisent:
UPDATES
partir d'un autre tx.INSERTS
et / ouDELETES
depuis un autre txNiveaux d'isolement avec différents types de lectures:
pour des exemples
la source
Vous ne voulez presque jamais l'utiliser
Read Uncommited
car il n'est pas vraimentACID
conforme.Read Commmited
est un bon point de départ par défaut.Repeatable Read
n'est probablement nécessaire que dans les scénarios de génération de rapports, de cumul ou d'agrégation. Notez que de nombreuses bases de données, postgres incluses, ne prennent pas réellement en charge la lecture répétable, vous devez utiliser à laSerializable
place.Serializable
est utile pour les choses dont vous savez qu'elles doivent se produire complètement indépendamment de toute autre chose; pensez-y commesynchronized
à Java. La sérialisation va de pair avec laREQUIRES_NEW
propagation.J'utilise
REQUIRES
pour toutes les fonctions qui exécutent des requêtes UPDATE ou DELETE ainsi que des fonctions de niveau "service". Pour les fonctions de niveau DAO qui exécutent uniquement des SELECT, j'utiliseSUPPORTS
qui participera à un TX si une est déjà démarrée (c'est-à-dire être appelée à partir d'une fonction de service).la source
L'isolement des transactions et la propagation des transactions, bien que liées, sont clairement deux concepts très différents. Dans les deux cas, les valeurs par défaut sont personnalisées au niveau du composant de limite client en utilisant la gestion des transactions déclarative ou la gestion des transactions programmatique . Les détails de chaque niveau d'isolement et attributs de propagation peuvent être trouvés dans les liens de référence ci-dessous.
Isolement de transaction
Pour deux transactions ou connexions en cours d'exécution ou plus vers une base de données, comment et quand les modifications apportées par les requêtes d'une transaction ont-elles un impact / sont-elles visibles pour les requêtes d'une transaction différente? Elle concernait également le type de verrouillage d'enregistrement de base de données qui sera utilisé pour isoler les modifications de cette transaction des autres transactions et vice versa. Ceci est généralement implémenté par la base de données / ressource qui participe à la transaction.
.
Propagation des transactions
Dans une application d'entreprise pour une demande / un traitement donné, de nombreux composants sont impliqués pour faire le travail. Certains de ces composants marquent les limites (début / fin) d'une transaction qui sera utilisée dans le composant respectif et ses sous-composants. Pour cette limite transactionnelle de composants, la proposition de transaction spécifie si le composant respectif participera ou non à la transaction et ce qui se passe si le composant appelant a déjà ou non une transaction déjà créée / démarrée. C'est la même chose que les attributs de transaction Java EE. Ceci est généralement implémenté par le gestionnaire de transactions / connexions client.
Référence:
Gestion des transactions de printemps
Isolement des transactions Wiki (systèmes de bases de données)
Oracle sur les niveaux d'isolement des transactions
Attributs de transaction Java EE (propagation)
Propagation des transactions Spring Framework
la source
J'ai couru
outerMethod
,method_1
etmethod_2
avec un mode de propagation différent.Voici la sortie pour différents modes de propagation.
Méthode externe
Méthode_1
Méthode_2
la source
On peut ajouter pour cela:
la source
Vous pouvez utiliser comme ceci:
Vous pouvez également utiliser cette chose:
la source