Cet article décrit la gestion des transactions dans Spring avec des exemples. Partagez-le avec tout le monde pour votre référence. L’analyse spécifique est la suivante :
Présentation de l'entreprise :
La gestion des transactions est une technologie essentielle dans le développement d'applications d'entreprise pour garantir l'intégrité et la cohérence des données.
Une transaction est une série d’actions traitées comme une seule unité de travail. Soit toutes ces actions fonctionnent, soit aucune ne fonctionne
Quatre attributs clés des transactions (ACID)
① Atomicité : Une opération atomique dans la salle des transactions consiste en une série d'actions. L'atomicité des transactions garantit que les actions sont soit entièrement terminées, soit n'ont aucun effet. ② Cohérence : une fois que toutes les actions de transaction sont terminées, la transaction est validée. Les données et les ressources sont dans un état cohérent qui répond aux règles métier ③ Isolement : de nombreuses transactions peuvent traiter les mêmes données en même temps, chaque élément doit donc être isolé des autres transactions pour éviter la corruption des données ④ Durabilité : une fois la transaction terminée , ses résultats ne devraient pas être affectés, quelles que soient les erreurs système qui se produisent. Généralement, les résultats d'une transaction sont écrits dans un stockage persistant
Gestion des transactions au printemps
En tant que framework d'applications d'entreprise, Spring définit une couche d'abstraction au-dessus de différentes API de gestion des transactions. Les développeurs d'applications n'ont pas besoin de comprendre l'API de gestion des transactions sous-jacente pour utiliser le mécanisme de gestion des transactions de Spring.
Spring prend en charge à la fois la gestion programmatique des transactions et la gestion déclarative des transactions.
Gestion des transactions programmatiques : intégrez le code de gestion des transactions dans les méthodes commerciales pour contrôler la soumission et l'annulation des transactions. Dans les transactions programmatiques, un code de gestion des transactions supplémentaire doit être inclus dans chaque opération commerciale.
Gestion déclarative des transactions : mieux à utiliser que la gestion programmatique des transactions dans la plupart des cas. Il sépare le code de gestion des transactions des méthodes métier et implémente la gestion des transactions de manière déclarative. La gestion des transactions, en tant que préoccupation transversale, peut être modularisée grâce aux méthodes AOP. Spring prend en charge la gestion déclarative des transactions via le framework Spring AOP.
Propriétés de propagation des transactions Spring :
Lorsqu'une méthode de transaction est appelée par une autre méthode de transaction, vous devez spécifier comment la transaction doit être propagée. Par exemple : la méthode peut continuer à s'exécuter dans la transaction existante, ou elle peut démarrer une nouvelle transaction et s'exécuter dans sa propre transaction.
Le comportement de propagation d'une transaction peut être spécifié par l'attribut de propagation. Spring définit 7 comportements de communication :
comportement de communication | signification |
PROPAGATION_MANDATOIRE | Indique que la méthode doit être exécutée dans une transaction. Si la transaction en cours n'existe pas, une exception sera levée. |
PROPAGATION_NESTED | Indique que si une transaction existe actuellement, cette méthode sera exécutée dans une transaction imbriquée. Les transactions imbriquées peuvent être validées ou annulées indépendamment de la transaction en cours. Si la transaction en cours n'existe pas, le comportement est le même que PROPAGATION_REQUIRED. Notez que la prise en charge par chaque fabricant de ce comportement de propagation est différente. Vous pouvez consulter la documentation de vos gestionnaires de ressources pour confirmer s'ils prennent en charge les transactions imbriquées. |
PROPAGATION_JAMAIS | Indique que la méthode actuelle ne doit pas être exécutée dans un contexte de transaction. Si une transaction est actuellement en cours, une exception sera levée |
PROPAGATION_NOT_SUPPORTED | Indique que cette méthode ne doit pas être exécutée dans une transaction. Si une transaction en cours existe, elle sera suspendue pendant l'exécution de cette méthode. Si vous utilisez JTATransactionManager, vous devez accéder à TransactionManager |
PROPAGATION_REQUIRED | Indique que la méthode actuelle doit être exécutée dans une transaction. Si la transaction en cours existe, la méthode sera exécutée dans cette transaction. Sinon, une nouvelle transaction sera démarrée |
PROPAGATION_REQUIRED_NEW | Indique que la méthode actuelle doit s'exécuter dans sa propre transaction. Une nouvelle transaction sera lancée. Si une transaction en cours existe, elle sera suspendue lors de l'exécution de cette méthode. Si vous utilisez JTATransactionManager, vous devez accéder à TransactionManager |
PROPAGATION_SUPPORTS | Indique que la méthode actuelle ne nécessite pas de contexte de transaction, mais s'il existe une transaction en cours, la méthode s'exécutera dans cette transaction |
Parmi eux, PROPAGATION_REQUIRED est l'attribut de propagation par défaut
Problèmes causés par des transactions simultanées
De nombreux problèmes inattendus peuvent survenir lorsque plusieurs transactions dans la même application ou dans différentes applications s'exécutent simultanément sur le même ensemble de données.
Les problèmes causés par des transactions simultanées peuvent être divisés dans les trois catégories suivantes :
① Lecture sale : une lecture sale se produit lorsqu'une transaction lit des données qui ont été réécrites par une autre transaction mais qui n'ont pas encore été validées. Si la réécriture est annulée ultérieurement, les données obtenues par la première transaction seront invalides.
② Lecture non répétable : une lecture non répétable se produit lorsqu'une transaction exécute la même requête deux fois ou plus, mais obtient des données différentes à chaque fois. Cela est généralement dû au fait qu'une autre transaction simultanée a mis à jour les données entre les requêtes.
③ Lecture fantôme : la lecture fantôme est similaire à la lecture non répétable. Cela se produit lorsqu'une transaction (T1) lit quelques lignes de données, puis qu'une autre transaction simultanée (T2) insère des données. Dans les requêtes suivantes, la première transaction (T1) découvrira qu'il existe d'autres enregistrements qui n'existaient pas à l'origine.
exemple de code
Le premier est la table de la base de données :
Y compris le livre (isbn, book_name, prix), le compte (nom d'utilisateur, solde), book_stock (isbn, stock)
Ensuite les classes utilisées :
LibrairieDao
Copiez le code comme suit :
paquet com.yl.spring.tx ;
interface publique BookShopDao {
//Obtenir le prix unitaire du livre en fonction du numéro du livre
public int findBookPriceByIsbn (String isbn);
//Mettre à jour l'inventaire du livre pour que l'inventaire correspondant au numéro du livre soit -1
public void updateBookStock (String isbn);
// Mettre à jour le solde du compte de l'utilisateur : créer le solde-prix du nom d'utilisateur
public void updateUserAccount (String nom d'utilisateur, prix int);
}
LibrairieDaoImpl
Copiez le code comme suit :
paquet com.yl.spring.tx ;
importer org.springframework.beans.factory.annotation.Autowired ;
importer org.springframework.jdbc.core.JdbcTemplate ;
importer org.springframework.stereotype.Repository ;
@Repository("bookShopDao")
classe publique BookShopDaoImpl implémente BookShopDao {
@Autowired
privé JdbcTemplate JdbcTemplate ;
@Outrepasser
public int findBookPriceByIsbn (String isbn) {
String sql = "SELECT price FROM book WHERE isbn = ?";
return JdbcTemplate.queryForObject(sql, Integer.class, isbn);
}
@Outrepasser
public void updateBookStock (String isbn) {
//Vérifie si l'inventaire du livre est suffisant, sinon, lève une exception
String sql2 = "SELECT stock FROM book_stock WHERE isbn = ?";
int stock = JdbcTemplate.queryForObject(sql2, Integer.class, isbn);
si (stock == 0) {
throw new BookStockException("Stock insuffisant!");
}
String sql = "UPDATE book_stock SET stock = stock - 1 WHERE isbn = ?";
JdbcTemplate.update(sql, isbn);
}
@Outrepasser
public void updateUserAccount (String nom d'utilisateur, prix int) {
//Vérifie si le solde est insuffisant, sinon lève une exception
String sql2 = "SELECT balance FROM account WHERE username = ?";
int balance = JdbcTemplate.queryForObject(sql2, Integer.class, nom d'utilisateur);
si (solde < prix) {
throw new UserAccountException("Solde insuffisant!");
}
String sql = "MISE À JOUR du compte SET balance = solde - ? WHERE nom d'utilisateur = ?";
JdbcTemplate.update(sql, prix, nom d'utilisateur);
}
}
LibrairieService
Copiez le code comme suit : package com.yl.spring.tx ;
interface publique BookShopService {
achat public nul (nom d'utilisateur String, isbn String);
}
BookShopServiceImpl
Copiez le code comme suit :
paquet com.yl.spring.tx ;
importer org.springframework.beans.factory.annotation.Autowired ;
importer org.springframework.stereotype.Service ;
importer org.springframework.transaction.annotation.Isolation ;
importer org.springframework.transaction.annotation.Propagation ;
importer org.springframework.transaction.annotation.Transactional ;
@Service("bookShopService")
classe publique BookShopServiceImpl implémente BookShopService {
@Autowired
BookShopDao privé bookShopDao;
/**
* 1. Ajouter des annotations de transaction
* Utiliser la propagation pour spécifier le comportement de propagation de la transaction, c'est-à-dire comment utiliser la transaction lorsque la méthode de transaction actuelle est appelée par une autre méthode de transaction.
* La valeur par défaut est OBLIGATOIRE, ce qui signifie utiliser la transaction appelant la méthode
* REQUIRES_NEW : utilisez votre propre transaction et la transaction de la méthode de transaction appelée est suspendue.
*
* 2. Utilisez l'isolation pour spécifier le niveau d'isolation de la transaction. La valeur la plus couramment utilisée est READ_COMMITTED.
* 3. Par défaut, la transaction déclarative de Spring annule toutes les exceptions d'exécution, qui peuvent également être définies via les propriétés correspondantes. Normalement, la valeur par défaut est suffisante.
* 4. Utilisez readOnly pour spécifier si la transaction est en lecture seule. Indique que cette transaction lit uniquement les données mais ne met pas à jour les données, ce qui peut aider le moteur de base de données à optimiser la transaction. S'il s'agit réellement d'une méthode qui lit uniquement la base de données, readOnly=true doit être défini.
* 5. Utilisez timeOut pour spécifier le temps que peut prendre une transaction avant de forcer une restauration.
*/
@Transactional(propagation=Propagation.REQUIRES_NEW,
isolation=Isolation.READ_COMMITTED,
noRollbackFor={UserAccountException.class},
readOnly=true, délai d'attente=3)
@Outrepasser
achat public annulé (nom d'utilisateur String, isbn String) {
//1. Obtenez le prix unitaire du livre
prix int = bookShopDao.findBookPriceByIsbn(isbn);
//2. Mettre à jour l'inventaire des livres
bookShopDao.updateBookStock(isbn);
//3. Mettre à jour le solde utilisateur
bookShopDao.updateUserAccount(nom d'utilisateur, prix);;
}
}
BookStockException
Copiez le code comme suit :
paquet com.yl.spring.tx ;
la classe publique BookStockException étend RuntimeException {
/**
*
*/
privé statique final long serialVersionUID = 1L ;
public BookStockException() {
super();
// TODO Stub de constructeur généré automatiquement
}
public BookStockException (String arg0, Throwable arg1, boolean arg2,
booléen arg3) {
super(arg0, arg1, arg2, arg3);
// TODO Stub de constructeur généré automatiquement
}
public BookStockException (String arg0, Throwable arg1) {
super(arg0, arg1);
// TODO Stub de constructeur généré automatiquement
}
public BookStockException (Chaîne arg0) {
super(arg0);
// TODO Stub de constructeur généré automatiquement
}
public BookStockException (Throwable arg0) {
super(arg0);
// TODO Stub de constructeur généré automatiquement
}
}
Exception de compte utilisateur
Copiez le code comme suit :
paquet com.yl.spring.tx ;
la classe publique UserAccountException étend RuntimeException {
/**
*
*/
privé statique final long serialVersionUID = 1L ;
public UserAccountException() {
super();
// TODO Stub de constructeur généré automatiquement
}
public UserAccountException (String arg0, Throwable arg1, boolean arg2,
booléen arg3) {
super(arg0, arg1, arg2, arg3);
// TODO Stub de constructeur généré automatiquement
}
public UserAccountException (String arg0, Throwable arg1) {
super(arg0, arg1);
// TODO Stub de constructeur généré automatiquement
}
public UserAccountException (String arg0) {
super(arg0);
// TODO Stub de constructeur généré automatiquement
}
public UserAccountException (Throwable arg0) {
super(arg0);
// TODO Stub de constructeur généré automatiquement
}
}
Caissier
Copiez le code comme suit :
paquet com.yl.spring.tx ;
importer java.util.List ;
interface publique Caissier {
caisse publique annulée (nom d'utilisateur String, List <String> isbns);
}
CaissierImpl. CashierImpl.checkout et bookShopService.purchase testent conjointement le comportement de propagation des transactions
Copiez le code comme suit :
paquet com.yl.spring.tx ;
importer java.util.List ;
importer org.springframework.beans.factory.annotation.Autowired ;
importer org.springframework.stereotype.Service ;
importer org.springframework.transaction.annotation.Transactional ;
@Service("caissier")
classe publique CashierImpl implémente Cashier {
@Autowired
BookShopService privé bookShopService ;
@Transactionnel
@Outrepasser
public void checkout (String nom d'utilisateur, List<String> isbns) {
pour(Chaîne isbn : isbns) {
bookShopService.purchase (nom d'utilisateur, isbn);
}
}
}
Classe d'essai :
Copiez le code comme suit :
paquet com.yl.spring.tx ;
importer java.util.Arrays ;
importer org.junit.Test ;
importer org.springframework.context.ApplicationContext ;
importer org.springframework.context.support.ClassPathXmlApplicationContext ;
classe publique SpringTransitionTest {
Private ApplicationContext ctx = null ;
privé BookShopDao bookShopDao = null;
bookShopService privé bookShopService = null;
caissier privé = null ;
{
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
bookShopDao = ctx.getBean(BookShopDao.class);
bookShopService = ctx.getBean(BookShopService.class);
caissier = ctx.getBean(Cashier.class);
}
@Test
public void testBookShopDaoFindPriceByIsbn() {
System.out.println(bookShopDao.findBookPriceByIsbn("1001"));
}
@Test
public void testBookShopDaoUpdateBookStock(){
bookShopDao.updateBookStock("1001");
}
@Test
public void testBookShopDaoUpdateUserAccount(){
bookShopDao.updateUserAccount("AA", 100);
}
@Test
public void testBookShopService(){
bookShopService.purchase("AA", "1001");
}
@Test
public void testTransactionPropagation(){
cashier.checkout("AA", Arrays.asList("1001", "1002"));
}
}
J'espère que cet article sera utile à la conception de la programmation Spring de chacun.