Printemps @ transactionnel-isolation, propagation
peut quelqu'un expliquer ce que isolation & propagation paramètres sont pour dans le @Transactional
annotation via l'exemple du monde réel. Fondamentalement, quand et pourquoi je devrais choisir de changer leurs valeurs par défaut.
10 réponses
bonne question, mais pas anodine à répondre.
définit le lien entre les transactions. Options communes
-
Required
: le Code s'exécute toujours dans une transaction. Créez une nouvelle transaction ou réutilisez-en une si disponible. -
Requires_new
: le Code s'exécute toujours dans une nouvelle transaction. Suspendre la transaction en cours s'il en existe une.
définit le contrat de données entre les transactions.
-
Read Uncommitted
: permet de lire sale -
Read Committed
: ne permet pas le dirty lit -
Repeatable Read
: si une ligne est lue deux fois dans la même transaction, le résultat sera toujours le même -
Serializable
: exécute toutes les opérations dans une séquence
les différents niveaux ont des caractéristiques de performance différentes dans une application multi-filetée. Je pense que si vous comprenez le concept dirty reads
vous pourrez choisir une bonne option.
exemple où une mauvaise lecture peut se produire
thread 1 thread 2
| |
write(x) |
| |
| read(x)
| |
rollback |
v v
value (x) is now dirty (incorrect)
donc un défaut sain (si tel peut être revendiqué) pourrait être Read Comitted
, qui ne permet de lire que des valeurs qui ont déjà été comitté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.
exemple pratique où une nouvelle transaction sera toujours créée lors de la saisie de la routine provideService
et complétée lors du 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 utilisé Required
à la place, l'opération restera ouverte si l'opération était déjà ouverte lors de la saisie de la routine.
Notez également que le résultat d'un rollback
pourrait être différent car plusieurs exécutions pourraient prendre part à 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 du
-
Requires new
on s'attendrait à ce quefooService.provideService()
soit et non soit retranché puisqu'il a créé sa propre sous-transaction. -
Required
nous nous attendions à ce que tout soit retourné et que le magasin de fond de caisse demeure inchangé.
PROPAGATION_REQUIRED = 0 ; si la source de données Transactionobject T1 est déjà commencée pour la méthode M1.Si, pour une autre méthode, un objet de Transaction M2 est requis ,Aucun nouvel objet de Transaction n'est créé .Même objet T1 est utilisé pour M2
PROPAGATION_MANDATORY = 2 ; la méthode doit être exécutée dans le cadre d'une transaction. Si aucun transaction existante est en cours, Une exception sera lancée
PROPAGATION_REQUIRES_NEW = 3 ; si data Sourcetransactionobject T1 est déjà démarré pour la méthode M1 et est en cours(exécution de la méthode M1) .Si une autre méthode M2 commence L'exécution, T1 est suspendu pour la durée de la méthode M2 avec de nouvelles données Sourcetransactionobject T2 pour M2.M2 exécuté dans son propre contexte de transaction
PROPAGATION_NOT_SUPPORTED = 4 ; si la source de Donnéestransactionobject T1 est déjà commencée pour la méthode M1.Si un autre la méthode M2 est appliquée simultanément .Alors M2 ne devrait 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 le contexte d'une transaction.
un niveau d'isolement: C'est au sujet de combien de transaction peut être touchées par les activités des autres concurrentes transactions.Il a soutient la cohérence laissant les données parmi de nombreux tableaux 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 avec les multiples de transaction
scénario 1 .Si l'opération T1 lit les données du tableau A1 qui ont été écrites par une autre opération T2 concurrente.Si, Sur le chemin T2 est le retour en arrière,les données obtenues par T1 n'est pas valide.Par exemple, a=2 est une donnée originale .Si T1 lire a=1 qui a été écrit par T2.Si T2 rollback alors a=1 sera rollback a=2 en DB.Mais, maintenant, T1 A A=1 mais en DB tableau il est remplacé par a=2.
Scénario 2 .Si l'opération T1 lit les données du tableau A1.Si une autre opération concurrente(T2) met à jour les données du tableau A1.Alors les données que T1 a lues sont différent du tableau A1.Parce que T2 a mis à jour les données du tableau A1.Par exemple, si T1 lire a=1 et T2 mis à jour a=2.Puis un!=B.
scénario 3 .Si L'opération T1 lit les données du tableau A1 avec un certain nombre de lignes. Si une autre opération concurrente(T2) insère plus de lignes dans le tableau A1.Le le nombre de lignes lues par T1 diffère des lignes du tableau A1
le scénario 1 s'appelle lit sale.
le scénario 2 est appelé lectures non reproductibles.
scénario 3 s'appelle lit Le Fantôme.
ainsi, le niveau d'isolement est le extension à laquelle scénario 1, Scénario 2, scénario 3 peut être évité. Vous pouvez obtenir le niveau d'isolement complet en mettant en œuvre le verrouillage.Qui empêche les lectures simultanées et écrit aux mêmes données de se produire.Mais il affecte les performances .Le niveau d'isolation dépend de l'application à combien d'isolement est requis.
ISOLATION_READ_UNCOMMITTED : permet de lire les changements qui n'ont pas encore été engagé.Elle souffre du scénario 1, du scénario 2, du scénario 3
ISOLATION_READ_COMMITTED : permet de lire des transactions simultanées qui ont été commises. Elle peut souffrir du scénario 2 et du scénario 3. Parce que d'autres transactions peuvent mettre à jour les données.
ISOLATION_REPEATABLE_READ : plusieurs lectures du même champ donneront les mêmes résultats jusqu'à ce qu'il soit modifié par lui-même.Il peut souffrir de Le scénario 3.Parce d'autres transactions peuvent être l'insertion des données
ISOLATION_SERIALISABLE : scénario 1,Scénario 2,scénario 3 n'arrive jamais.C'est un isolement complet.Cela implique un verrouillage complet.Il affecte performace en raison de 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.
assez d'explications sur chaque paramètre est donné par d'autres réponses; cependant, vous avez demandé un exemple du monde réel, voici celui qui clarifie le but de propagation options:
Supposons que vous soyez en charge de la mise en œuvre d'un service d'inscription dans lequel un e-mail de confirmation est envoyé à l'utilisateur. Vous arrivez avec deux objets de service, un pour inscription l'utilisateur et un pour envoyer des e-mails , que ce dernier est appelé à l'intérieur du premier. Par exemple quelque chose comme ceci:/* Sign Up service */
@Service
@Transactional(Propagation=REQUIRED)
class SignUpService{
...
void SignUp(User user){
...
emailService.sendMail(User);
}
}
/* E-Mail Service */
@Service
@Transactional(Propagation=REQUIRES_NEW)
class EmailService{
...
void sendMail(User user){
try{
... // Trying to send the e-mail
}catch( Exception)
}
}
vous avez peut-être remarqué que le second service est de type propagation REQUIRES_NEW et il y a de plus de chances qu'il lance une exception (serveur SMTP en panne ,e-mail invalide ou autres raisons).Vous ne voulez probablement pas que l'ensemble du processus de revenir en arrière, comme supprimer les informations de l'utilisateur de la base de données ou d'autres choses; par conséquent, vous appelez le deuxième service dans une transaction distincte.
retour à notre exemple, cette fois vous êtes préoccupé par la sécurité de la base de données, donc vous définissez vos classes DAO de cette façon:/* User DAO */
@Transactional(Propagation=MANDATORY)
class UserDAO{
// some CRUD methods
}
ce qui signifie que chaque fois qu'un objet DAO, et donc un accès potentiel à la base de données, est créé, nous devons rassurer que l'appel a été fait à partir de l'intérieur de l'un de nos services, ce qui implique qu'une transaction en direct devrait exister; sinon, une exception se produit.Par conséquent, la propagation est du type Obligatoire .
Isolation level définit comment les changements apportés à un dépôt de données par une transaction affectent d'autres transactions simultanées simultanées, et aussi comment et quand les données modifiées deviennent disponibles pour d'autres transactions. Lorsque nous définissons une transaction en utilisant le cadre de printemps, nous sommes également en mesure de configurer dans quel niveau d'isolation cette même transaction sera exécutée.
@Transactional(isolation=Isolation.READ_COMMITTED)
public void someTransactionalMethod(Object obj) {
}
read_uncommitted isolation level indique que une transaction peut lire des données qui ne sont pas encore engagées par d'autres transactions.
read_committed isolation level indique qu'une transaction ne peut pas lire des données qui ne sont pas encore engagées par d'autres transactions.
Le niveau d'isolementRÉPÉTABLE_READ indique que si une transaction lit plusieurs fois un enregistrement de la base de données, le résultat de toutes les opérations de lecture doit toujours être le même.
le niveau D'isolement sérialisable est le le plus restrictif de tous les niveaux d'isolement. Les Transactions sont exécutées avec verrouillage à tous les niveaux (lecture, portée et écriture de verrouillage) de sorte qu'ils apparaissent comme s'ils ont été exécutés d'une manière sérialisée.
Propagation est la capacité de décider comment les méthodes commerciales doivent être encapsulées dans les transactions logiques ou physiques.
Spring REQUIRED behavior signifie que la même transaction sera utilisée s'il y a déjà une opération ouverte transaction dans le contexte actuel d'exécution de la méthode bean.
REQUIRES_NEW behavior signifie qu'une nouvelle transaction physique sera toujours créée par le conteneur.
le comportement imbriqué rend les transactions à ressort imbriquées pour utiliser la même transaction physique, mais fixe des points d'économies entre les invocations imbriquées de sorte que les transactions internes peuvent également se rétracter indépendamment des transactions externes.
le comportement obligatoire stipule qu'un la transaction ouverte existante doit déjà exister. Sinon une exception sera levée par le conteneur.
the NEVER behavior déclare 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_SUPPORTÉ s'exécutera en dehors de la portée de toute transaction. Si une transaction ouverte existe déjà, elle sera interrompue.
les SUPPORTS behavior exécutera dans le cadre d'une transaction si une transaction ouverte existe déjà. S'il n'y a pas une transaction déjà ouverte, la méthode s'exécutera de toute façon, mais de manière non transactionnelle.
vous ne voulez presque jamais utiliser Read Uncommited
car il n'est pas vraiment ACID
conforme. Read Commmited
est un bon point de départ. Repeatable Read
n'est probablement nécessaire que dans les scénarios de déclaration, de rollup ou d'agrégation. Notez que beaucoup de DBs, postgres inclus ne supportent pas réellement la lecture répétable, vous devez utiliser Serializable
à la place. Serializable
est utile pour des choses que vous savez devoir se produire complètement indépendamment de toute autre chose; pensez-y comme synchronized
en Java. Serialisable va de pair avec la propagation REQUIRES_NEW
.
j'utilise REQUIRES
pour toutes les fonctions qui exécutent des requêtes de mise à jour ou de suppression ainsi que des fonctions de niveau de" service". Pour les fonctions de niveau DAO qui ne fonctionnent que SELECTs, j'utilise SUPPORTS
qui participera à un TX si l'une d'elles est déjà lancée (c'est-à-dire appelée à partir d'une fonction de service).
l'Isolation de la Transaction et de la Transaction de Propagation bien que liés, mais sont clairement deux des concepts très différents. Dans les deux cas, les défauts de paiement sont personnalisés aux limites de la composante client soit en utilisant Declarative transaction management ou Programmatic transaction management . 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.
pour deux ou plusieurs transactions en cours d'exécution/connexions à une base de données, comment et quand les changements sont faits par les requêtes dans une transaction impact/visible aux requêtes dans une transaction différente. Elle portait également sur le type de verrouillage de la base de données qui sera utilisé pour isoler les changements dans cette transaction des autres transactions et vice versa. Cela est généralement mis en œuvre par la base de données/ressource qui participe à transaction.
.
dans une application d'entreprise pour n'importe quelle demande/traitement donné il y a beaucoup de composants qui sont impliqués pour obtenir le travail fait. Certains de ces composants marquer les limites (début/fin) d'une opération qui sera utilisé dans le composant et ses sous-composants. Pour cette limite transactionnelle des composantes, la Transaction Propogation spécifie si les composants respectifs participeront ou non à la transaction et ce qui se passera si calling component a déjà ou n'a pas une transaction déjà créée/commencée. Ceci est identique aux attributs de Transaction Java EE. Cela est généralement mis en œuvre par le client transaction/connection manager.
référence:
j'ai exécuté outerMethod
, method_1
et method_2
avec un mode de propagation différent.
ci-dessous est la sortie pour le mode de propagation différent.
-
Méthode Extérieure
@Transactional @Override public void outerMethod() { customerProfileDAO.method_1(); iWorkflowDetailDao.method_2(); }
-
Method_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()) { } } }
-
Method_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()) { } } }
-
- outerMethod-Without transaction
- method_1-Propagation.Obligatoire) -
- method_2 - Opération d'annotation seulement
- sortie: method_1 va jeter exception qu'aucune transaction existante
-
- outerMethod - Without transaction
- method_1 - Opération d'annotation seulement
- method_2 - Propagation.Obligatoire)
- sortie: method_2 va jeter exception qu'aucune transaction existante
- sortie: method_1 persistera l'enregistrement dans la base de données.
-
- outerMethod - de l'opération
- method_1 - Opération d'annotation seulement
- method_2-Propagation.Obligatoire)
- sortie: method_2 will persistent enregistrement dans la base de données.
- sortie: method_1 persistera l'enregistrement dans la base de données. -- Ici principale transaction externe existante utilisée à la fois pour la méthode 1 et 2
-
- outerMethod - de l'opération
- method_1-Propagation.Obligatoire) -
- method_2 - Opération d'annotation seulement et jette exception
- Sortie: Aucun enregistrement ne persiste dans la base de données signifie que le retour est fait.
-
- outerMethod - de l'opération
- method_1-Propagation.REQUIRES_NEW)
- method_2-Propagation.REQUIRES_NEW) et jette 1/0 exception
- sortie: method_2 va lancer exception donc method_2 enregistrement n'a pas persisté.
- sortie: method_1 persistera l'enregistrement dans la base de données.
- sortie: il N'y a pas de retour en arrière pour method_1
-
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
}
}
Une Transaction représente une unité de travail avec une base de données.
in spring TransactionDefinition
interface qui définit les propriétés de transaction compatibles avec Spring. @Transactional
annotation décrit les attributs de transaction sur une méthode ou 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 entre thread de communication)
+-------+---------------------------+------------------------------------------------------------------------------------------------------+
| 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 acides (atomicité, consistance, Isolation, durabilité) des transactions de base de données. L'isolement détermine la façon dont l'intégrité des transactions est visible pour les autres utilisateurs et systèmes. Il utilise le verrouillage des ressources, c'est-à-dire le contrôle de la concurrence, pour s'assurer qu'une seule transaction peut accéder à la ressource à un point donné.
perception de verrouillage: le niveau d'isolement détermine la durée de maintien des serrures.
+---------------------------+-------------------+-------------+-------------+------------------------+
| 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 |
+---------------------------+-------------------+-------------+-------------+------------------------+
Lire la perception: 3 sortes de problèmes majeurs se produit:
- Sale lit : lit les données non validées à partir d'un autre tx(transaction).
- Non répétable lit : lit commis
UPDATES
d'un autre tx. - Phantom lit : lit commis
INSERTS
et/ouDELETES
d'un autre tx
niveaux 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 |
+---------------------------+----------------+----------------------+----------------+
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();
}