Quelqu'un peut-il expliquer les paramètres isolement & propagation utilisés dans l'annotation @Transactional
via un exemple réel?
Fondamentalement, quand et pourquoi je devrais choisir de changer leurs valeurs par défaut.
Bonne question, mais pas triviale à répondre.
Définit les relations entre les transactions. Options communes:
Required
: Le code sera toujours exécuté dans une transaction. Crée une nouvelle transaction ou en réutilise une si disponible.Requires_new
: Le code sera toujours exécuté dans une nouvelle transaction. Suspend la transaction en cours s'il en existe une.Définit le contrat de données entre les transactions.
Read Uncommitted
: Autorise les lectures modifiées.Read Committed
: N'autorise pas les lectures modifiées.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 différentes caractéristiques de performance dans une application multithread. Je pense que si vous comprenez le concept dirty reads
, vous pourrez sélectionner une bonne option.
Exemple de cas où une lecture sale peut se produire:
thread 1 thread 2
| |
write(x) |
| |
| read(x)
| |
rollback |
v v
value (x) is now dirty (incorrect)
Ainsi, un défaut par défaut (si tel est le cas) peut être Read Committed
, qui ne vous permet de lire que les valeurs déjà validées par d'autres transactions en cours d'exécution, en combinaison avec un niveau de propagation de Required
. Vous pourrez ensuite travailler à partir de là si votre application a d’autres besoins.
Voici un exemple pratique où une nouvelle transaction sera toujours créée lors de la saisie de la routine provideService
et complétée lors de son départ:
public class FooService {
private Repository repo1;
private Repository repo2;
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void provideService() {
repo1.retrieveFoo();
repo2.retrieveFoo();
}
}
Si nous avions plutôt utilisé Required
, la transaction resterait ouverte si la transaction était déjà ouverte lors de la saisie de la routine. Notez également que le résultat de rollback
peut être différent car plusieurs exécutions peuvent prendre part à la même transaction.
Nous pouvons facilement vérifier le comportement avec un test et voir en quoi les résultats diffèrent selon 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 attendrions à ce que fooService.provideService()
soit PAS annulé car il a créé sa propre sous-transaction.
Required
: nous nous attendions à ce que tout ait été annulé et que le magasin de support ne soit pas modifié.
PROPAGATION_REQUIRED =; Si DataSourceTransactionObject T1 est déjà démarré pour la méthode M1.If pour un autre objet Transaction de méthode M2, aucun nouvel objet Transaction n'est créé. Le même objet T1 est utilisé pour M2.
PROPAGATION_MANDATORY = 2; La méthode doit être exécutée dans une transaction. Si aucune transaction existante n'est en cours, une exception sera levée
PROPAGATION_REQUIRES_NEW =; Si DataSourceTransactionObject T1 est déjà démarré pour la méthode M1 et en cours (exécution de la méthode M1). Si une autre méthode M2 commence à s'exécuter, T1 est suspendu pour la durée de la méthode M2 avec le 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.If si une autre méthode M2 est exécutée simultanément. Ensuite, M2 ne doit pas être exécuté 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 un contexte de transaction.
n niveau d'isolement: Il s'agit de l'impact qu'une transaction peut avoir sur les activités d'autres transactions simultanées. Elle prend en charge la cohérence en laissant les données de nombreuses tables dans un état cohérent. Cela implique le verrouillage des lignes et/ou des tables dans une base de données.
Le problème avec plusieurs transactions
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.If si T2 est rétrogradé, les données obtenues par T1 sont invalides. Un.Eg a = 2 est original données .Si T1 lit a = 1 qui a été écrit par T2.If 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 sur la table A1.Eg si T1 lit a = 1 et T2 mis à jour a = 2.Alors a! = b.
Scénario. 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 davantage de lignes dans la table A1.Le nombre de lignes lues par T1 est différent de celui de la table A1
Le scénario 1 s'appelle Lectures sales.
Le scénario 2 s'appelle lectures non répétables.
Le scénario 3 s'appelle Phantom reads.
Donc, le niveau d'isolement est l'étendue jusqu'à laquelle Scénario 1, Scénario 2, Scénario peut être évité. Vous pouvez obtenir un niveau d'isolation complet en mettant en oeuvre le verrouillage.Cela empêche les lectures et les écritures simultanées sur les mêmes données.Mais cela affecte les performances. Le niveau d'isolation dépend de l'application à l'application du degré d'isolation requis.
ISOLATION_READ_UNCOMMITTED: Permet de lire les modifications qui n’ont pas encore été validées. Il est affecté par les scénarios 1, 2 et 3.
ISOLATION_READ_COMMITTED: Autorise les lectures à partir de transactions simultanées validées. Les scénarios 2 et 3 peuvent en souffrir, car 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 être affecté par le scénario 3.Parce que d'autres transactions peuvent être en train d'insérer les données
ISOLATION_SERIALIZABLE: Le scénario 1, scénario 2, scénario 3 ne se produit jamais.Il est une isolation complète.Il implique un verrouillage complet.Il entraîne l'exécution à cause du verrouillage.
Vous pouvez tester en utilisant
public class TransactionBehaviour {
// set is either using xml Or annotation
DataSourceTransactionManager manager=new DataSourceTransactionManager();
SimpleTransactionStatus status=new SimpleTransactionStatus();
;
public void beginTransaction()
{
DefaultTransactionDefinition Def = new DefaultTransactionDefinition();
// overwrite default PROPAGATION_REQUIRED and ISOLATION_DEFAULT
// set is either using xml Or annotation
manager.setPropagationBehavior(XX);
manager.setIsolationLevelName(XX);
status = manager.getTransaction(Def);
}
public void commitTransaction()
{
if(status.isCompleted()){
manager.commit(status);
}
}
public void rollbackTransaction()
{
if(!status.isCompleted()){
manager.rollback(status);
}
}
Main method{
beginTransaction()
M1();
If error(){
rollbackTransaction()
}
commitTransaction();
}
}
Vous pouvez déboguer et voir le résultat avec différentes valeurs pour l'isolation et la propagation.
Niveau d'isolement définit comment les modifications apportées à certains référentiels de données par une transaction affectent d'autres transactions simultanées simultanées, ainsi que le moment et la date à laquelle les données modifiées deviennent disponibles pour les autres transactions. Lorsque nous définissons une transaction à l'aide de la structure Spring, nous sommes également en mesure de configurer le niveau d'isolation dans lequel cette même transaction sera exécutée.
@Transactional(isolation=Isolation.READ_COMMITTED)
public void someTransactionalMethod(Object obj) {
}
Le niveau d'isolation READ_UNCOMMITTED indique qu'une transaction peut lire des données non encore validées par d'autres transactions.
Le niveau d'isolation READ_COMMITTED indique qu'une transaction ne peut pas lire les données qui n'ont pas encore été validées par d'autres transactions.
Le niveau d'isolation REPEATABLE_READ indique que si une transaction lit un enregistrement de la base de données plusieurs fois, le résultat de toutes ces opérations de lecture doit toujours être identique.
Le niveau d'isolement SERIALIZABLE est le plus restrictif de tous les niveaux d'isolement. Les transactions sont exécutées avec un verrouillage à tous les niveaux (lecture, étendue et verrouillage en écriture), de sorte qu'elles apparaissent comme si elles avaient été exécutées de manière sérialisée.
Propagation est la capacité de décider de la manière dont les méthodes métier doivent être encapsulées dans des 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 de la méthode bean en cours.
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 restaurées indépendamment des transactions externes.
Le comportement MANDATORY indique qu'une transaction ouverte existante doit déjà exister. Sinon, une exception sera lancé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 s'il existe déjà une transaction ouverte. S'il n'y a pas de transaction déjà ouverte, la méthode sera exécutée de toute façon, mais de manière non transactionnelle.
Vous ne voulez presque jamais utiliser Read Uncommited
car ce n'est pas vraiment conforme à ACID
. 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, y compris postgres, ne prennent pas en charge la lecture répétable, vous devez utiliser Serializable
à la place. Serializable
est utile pour les choses que vous savez devoir se passer de manière totalement indépendante de toute autre chose; Pensez-y comme synchronized
en Java. Sérialisable va de pair avec la propagation REQUIRES_NEW
.
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 n’exécutent que des commandes SELECT, j’utilise SUPPORTS
, qui participera à une émission si elle est déjà démarrée (c’est-à-dire appelée depuis une fonction de service).
Une transaction représente une unité de travail avec une base de données.
Au printemps TransactionDefinition
interface qui définit les propriétés de transaction conformes à Spring. L'annotation @Transactional
décrit les attributs de transaction d'une méthode ou d'une classe.
@Autowired
private TestDAO testDAO;
@Transactional(propagation=TransactionDefinition.PROPAGATION_REQUIRED,isolation=TransactionDefinition.ISOLATION_READ_UNCOMMITTED)
public void someTransactionalMethod(User user) {
// Interact with testDAO
}
Propagation (Reproduction): est utilisé pour la relation entre transactions. (analogue à Java communication entre threads)
+-------+---------------------------+------------------------------------------------------------------------------------------------------+
| value | Propagation | Description |
+-------+---------------------------+------------------------------------------------------------------------------------------------------+
| -1 | TIMEOUT_DEFAULT | Use the default timeout of the underlying transaction system, or none if timeouts are not supported. |
| 0 | PROPAGATION_REQUIRED | Support a current transaction; create a new one if none exists. |
| 1 | PROPAGATION_SUPPORTS | Support a current transaction; execute non-transactionally if none exists. |
| 2 | PROPAGATION_MANDATORY | Support a current transaction; throw an exception if no current transaction exists. |
| 3 | PROPAGATION_REQUIRES_NEW | Create a new transaction, suspending the current transaction if one exists. |
| 4 | PROPAGATION_NOT_SUPPORTED | Do not support a current transaction; rather always execute non-transactionally. |
| 5 | PROPAGATION_NEVER | Do not support a current transaction; throw an exception if a current transaction exists. |
| 6 | PROPAGATION_NESTED | Execute within a nested transaction if a current transaction exists. |
+-------+---------------------------+------------------------------------------------------------------------------------------------------+
Isolation: L'isolation est l'une des propriétés ACID (Atomicité, Consistance, Isolation, Durabilité) des transactions de base de données. L'isolement détermine la manière dont l'intégrité de la transaction est visible pour les autres utilisateurs et systèmes. Il utilise pour le verrouillage des ressources, c'est-à-dire le contrôle de simultanéité, assurez-vous qu'une seule transaction peut accéder à la ressource à un moment donné.
Perception de verrouillage: le niveau d'isolement détermine la durée pendant laquelle les verrous sont maintenus.
+---------------------------+-------------------+-------------+-------------+------------------------+
| Isolation Level Mode | Read | Insert | Update | Lock Scope |
+---------------------------+-------------------+-------------+-------------+------------------------+
| READ_UNCOMMITTED | uncommitted data | Allowed | Allowed | No Lock |
| READ_COMMITTED (Default) | committed data | Allowed | Allowed | Lock on Committed data |
| REPEATABLE_READ | committed data | Allowed | Not Allowed | Lock on block of table |
| SERIALIZABLE | committed data | Not Allowed | Not Allowed | Lock on full table |
+---------------------------+-------------------+-------------+-------------+------------------------+
Perception de lecture: se produisent les 3 types de problèmes majeurs suivants:
UPDATES
d'une autre tx.INSERTS
et/ou DELETES
d'un autre txNiveaux d'isolement avec différents types de lectures:
+---------------------------+----------------+----------------------+----------------+
| Isolation Level Mode | Dirty reads | Non-repeatable reads | Phantoms reads |
+---------------------------+----------------+----------------------+----------------+
| READ_UNCOMMITTED | allows | allows | allows |
| READ_COMMITTED (Default) | prevents | allows | allows |
| REPEATABLE_READ | prevents | prevents | allows |
| SERIALIZABLE | prevents | prevents | prevents |
+---------------------------+----------------+----------------------+----------------+
L'isolation de transaction et la propagation de transaction sont certes liées mais sont clairement deux concepts très différents. Dans les deux cas, les valeurs par défaut sont personnalisées au niveau du composant frontière du client, soit à l'aide de gestion des transactions déclarative ou gestion des transactions par programme . Vous trouverez des détails sur chaque niveau d’isolation et attribut de propagation dans les liens de référence ci-dessous.
Pour deux ou plusieurs transactions/connexions en cours d'exécution avec une base de données, comment et quand les modifications apportées par les requêtes d'une transaction sont-elles visibles/visibles pour les requêtes d'une autre transaction? Cela concernait également le type de verrouillage des enregistrements de la base de données utilisé pour isoler les modifications de cette transaction des autres transactions, et inversement. Ceci est généralement implémenté par la base de données/ressource qui participe à la transaction.
.
Dans une application d'entreprise pour une requête/un traitement donné, de nombreux composants sont impliqués dans l'exécution du travail. Certains de ces composants marquent les limites (début/fin) d'une transaction qui sera utilisée dans le composant et ses sous-composants respectifs. Pour cette limite transactionnelle de composants, Transaction Propogation spécifie si le composant respectif participera ou non à la transaction et ce qui se passera si le composant appelant a déjà créé ou non une transaction. Identique à JavaEE Transact Attributes. Ceci est généralement implémenté par le gestionnaire de transactions/connexions client.
Référence:
J'ai exécuté outerMethod
, method_1
et method_2
avec un mode de propagation différent.
Vous trouverez ci-dessous la sortie pour différents modes de propagation.
Méthode externe
@Transactional
@Override
public void outerMethod() {
customerProfileDAO.method_1();
iWorkflowDetailDao.method_2();
}
Méthode_1
@Transactional(propagation=Propagation.MANDATORY)
public void method_1() {
Session session = null;
try {
session = getSession();
Temp entity = new Temp(0l, "XXX");
session.save(entity);
System.out.println("Method - 1 Id "+entity.getId());
} finally {
if (session != null && session.isOpen()) {
}
}
}
Méthode_2
@Transactional()
@Override
public void method_2() {
Session session = null;
try {
session = getSession();
Temp entity = new Temp(0l, "CCC");
session.save(entity);
int i = 1/0;
System.out.println("Method - 2 Id "+entity.getId());
} finally {
if (session != null && session.isOpen()) {
}
}
}
Nous pouvons ajouter pour cela:
@Transactional(readOnly = true)
public class Banking_CustomerService implements CustomerService {
public Customer getDetail(String customername) {
// do something
}
// these settings have precedence for this method
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateCustomer(Customer customer) {
// do something
}
}
Vous pouvez utiliser comme ceci:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public EventMessage<ModificaOperativitaRapporto> activate(EventMessage<ModificaOperativitaRapporto> eventMessage) {
//here some transaction related code
}
Vous pouvez utiliser cette chose aussi:
public interface TransactionStatus extends SavepointManager {
boolean isNewTransaction();
boolean hasSavepoint();
void setRollbackOnly();
boolean isRollbackOnly();
void flush();
boolean isCompleted();
}