Spring @Transactional - 隔离、传播

发布于 2024-12-20 21:44:35 字数 139 浏览 3 评论 0原文

有人可以解释一下隔离和“隔离”吗?通过实际示例来了解 @Transactional 注释中的传播参数?

基本上什么时候以及为什么我应该选择更改它们的默认值?

Can someone explain the isolation & propagation parameters in the @Transactional annotation via a real-world example?

Basically when and why I should choose to change their default values?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

天暗了我发光 2024-12-27 21:44:35

好问题,尽管回答起来并不简单。

传播

定义如何传播交易是相互关联的。常见选项:

  • REQUIRED:代码将始终在事务中运行。创建一项新事务或重用一项(如果可用)。
  • REQUIRES_NEW:代码将始终在新事务中运行。如果存在当前事务,则暂停该事务。

@Transactional 的默认值是 REQUIRED,这通常是您想要的。

隔离

定义数据契约交易之间。

  • ISOLATION_READ_UNCOMMITTED:允许脏读。
  • ISOLATION_READ_COMMITTED:不允许脏读。
  • ISOLATION_REPEATABLE_READ:如果在同一个事务中读取同一行两次,结果将始终相同。
  • ISOLATION_SERIALIZABLE:按顺序执行所有事务。

在多线程应用程序中,不同级别具有不同的性能特征。我认为如果您理解脏读概念,您将能够选择一个好的选项。

不同数据库之间的默认值可能有所不同。例如,对于 MariaDB 它是可重复读取


脏读何时可能发生的示例:

  thread 1   thread 2      
      |         |
    write(x)    |
      |         |
      |        read(x)
      |         |
    rollback    |
      v         v 
           value (x) is now dirty (incorrect)

因此,合理的默认值(如果可以声明的话)可以是 ISOLATION_READ_COMMITTED,它只允许您读取其他正在运行的事务已经提交的值,并结合传播级别为REQUIRED。然后,如果您的应用程序有其他需求,您可以从那里开始工作。


一个实际示例,说明在进入 provideService 例程时始终会创建新事务并在离开时完成:

public class FooService {
    private Repository repo1;
    private Repository repo2;

    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public void provideService() {
        repo1.retrieveFoo();
        repo2.retrieveFoo();
    }
}

如果我们改为使用 REQUIRED,则事务 将保持打开状态
另请注意,回滚的结果可能会有所不同,因为多个执行可能会参与同一事务。


我们可以通过测试轻松验证行为,并查看结果如何随传播级别的不同而变化:

@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 ... 
}

传播级别为

  • REQUIRES_NEW 时:我们期望 fooService.provideService()由于创建了自己的子事务,因此回滚。

  • 必需:我们希望所有内容都回滚并且后备存储保持不变。

Good question, although not a trivial one to answer.

Propagation

Defines how transactions relate to each other. Common options:

  • REQUIRED: Code will always run in a transaction. Creates a new transaction or reuses one if available.
  • REQUIRES_NEW: Code will always run in a new transaction. Suspends the current transaction if one exists.

The default value for @Transactional is REQUIRED, and this is often what you want.

Isolation

Defines the data contract between transactions.

  • ISOLATION_READ_UNCOMMITTED: Allows dirty reads.
  • ISOLATION_READ_COMMITTED: Does not allow dirty reads.
  • ISOLATION_REPEATABLE_READ: If a row is read twice in the same transaction, the result will always be the same.
  • ISOLATION_SERIALIZABLE: Performs all transactions in a sequence.

The different levels have different performance characteristics in a multi-threaded application. I think if you understand the dirty reads concept you will be able to select a good option.

Defaults may vary between difference databases. As an example, for MariaDB it is REPEATABLE READ.


Example of when a dirty read can occur:

  thread 1   thread 2      
      |         |
    write(x)    |
      |         |
      |        read(x)
      |         |
    rollback    |
      v         v 
           value (x) is now dirty (incorrect)

So a sane default (if such can be claimed) could be ISOLATION_READ_COMMITTED, which only lets you read values which have already been committed by other running transactions, in combination with a propagation level of REQUIRED. Then you can work from there if your application has other needs.


A practical example of where a new transaction will always be created when entering the provideService routine and completed when leaving:

public class FooService {
    private Repository repo1;
    private Repository repo2;

    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public void provideService() {
        repo1.retrieveFoo();
        repo2.retrieveFoo();
    }
}

Had we instead used REQUIRED, the transaction would remain open if the transaction was already open when entering the routine.
Note also that the result of a rollback could be different as several executions could take part in the same transaction.


We can easily verify the behaviour with a test and see how results differ with propagation levels:

@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 ... 
}

With a propagation level of

  • REQUIRES_NEW: we would expect fooService.provideService() was NOT rolled back since it created its own sub-transaction.

  • REQUIRED: we would expect everything was rolled back and the backing store was unchanged.

她如夕阳 2024-12-27 21:44:35

PROPAGATION_REQUIRED = 0;如果 DataSourceTransactionObject T1 已针对方法 M1 启动。如果需要另一个方法 M2 Transaction 对象,则不会创建新的 Transaction 对象。 M2 使用相同的对象 T1。

PROPAGATION_MANDATORY = 2;方法必须在事务内运行。如果没有
现有事务正在进行中,将抛出异常。

PROPAGATION_REQUIRES_NEW = 3;如果 DataSourceTransactionObject T1 已针对方法 M1 启动并且正在进行中(执行方法 M1)。如果另一个方法 M2 开始执行,则 T1 在方法 M2 的持续时间内暂停,并为 M2 提供新的 DataSourceTransactionObject T2。 M2 在其自己的事务上下文中运行。

PROPAGATION_NOT_SUPPORTED = 4;如果 DataSourceTransactionObject T1 已针对方法 M1 启动。如果同时运行另一个方法M2。那么 M2 不应该在事务上下文中运行。 T1 暂停,直到 M2 完成。

PROPAGATION_NEVER = 5;这些方法都没有在事务上下文中运行。


隔离级别:
它是关于一个事务可能受到其他并发事务的活动影响的程度。它支持一致性,使多个表中的数据保持一致状态。它涉及锁定数据库中的行和/或表。

多笔交易的问题

场景1。如果 T1 事务从表 A1 中读取由另一个并发事务 T2 写入的数据。如果途中T2回滚,则T1获取到的数据是无效的。例如a=2是原始数据。如果T1读取到T2写入的a=1。如果T2回滚,那么a=1将回滚到DB中的a=2。但是,现在T1的a=1,但在DB表中它被更改为a=2。

场景2。如果T1事务从表A1读取数据。如果另一个并发事务(T2)更新表A1上的数据。那么T1读取到的数据为
与表A1不同。因为T2已经更新了表A1上的数据。例如,如果 T1 读取 a=1 并且 T2 更新 a=2。那么a!=b。

场景 3。如果T1事务从表A1中读取一定行数的数据。如果另一个并发事务 (T2) 在表 A1 上插入更多行。这
T1 读取的行数与表 A1 上的行数不同。

场景 1 称为“脏读”。

场景 2 称为“不可重复读”。

场景 3 称为“幻读”。

因此,隔离级别是延伸到可以预防场景1、场景2、场景3
您可以通过实现锁定来获得完全的隔离级别。这可以防止发生对同一数据的并发读取和写入。但它影响性能。隔离级别取决于应用程序之间需要多少隔离。

ISOLATION_READ_UNCOMMITTED:允许读取尚未提交的更改。它受到场景 1、场景 2、场景 3 的影响。

ISOLATION_READ_COMMITTED:允许从已提交的并发事务中读取。它可能会受到场景2和场景3的影响。因为其他事务可能正在更新数据。

ISOLATION_REPEATABLE_READ:同一字段的多次读取将产生相同的结果,直到它自行更改。它可能会遇到场景 3 的情况。因为其他事务可能正在插入数据。

ISOLATION_SERIALIZABLE:场景 1、场景 2、场景 3 永远不会发生。这是完全的隔离。它涉及完全锁定。由于锁定,它会影响性能。

您可以使用以下方式进行测试:

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();
    }
   
}

您可以使用不同的隔离和传播值进行调试并查看结果。

PROPAGATION_REQUIRED = 0; If DataSourceTransactionObject T1 is already started for Method M1. If for another Method M2 Transaction object is required, no new Transaction object is created. Same object T1 is used for M2.

PROPAGATION_MANDATORY = 2; method must run within a transaction. If no
existing transaction is in progress, an exception will be thrown.

PROPAGATION_REQUIRES_NEW = 3; If DataSourceTransactionObject T1 is already started for Method M1 and it is in progress (executing method M1). If another method M2 start executing then T1 is suspended for the duration of method M2 with new DataSourceTransactionObject T2 for M2. M2 run within its own transaction context.

PROPAGATION_NOT_SUPPORTED = 4; If DataSourceTransactionObject T1 is already started for Method M1. If another method M2 is run concurrently. Then M2 should not run within transaction context. T1 is suspended till M2 is finished.

PROPAGATION_NEVER = 5; None of the methods run in transaction context.


An isolation level:
It is about how much a transaction may be impacted by the activities of other concurrent transactions. It a supports consistency leaving the data across many tables in a consistent state. It involves locking rows and/or tables in a database.

The problem with multiple transaction

Scenario 1. If T1 transaction reads data from table A1 that was written by another concurrent transaction T2. If on the way T2 is rollback, the data obtained by T1 is invalid one. E.g. a=2 is original data. If T1 read a=1 that was written by T2. If T2 rollback then a=1 will be rollback to a=2 in DB. But, now, T1 has a=1 but in DB table it is changed to a=2.

Scenario2. If T1 transaction reads data from table A1. If another concurrent transaction (T2) update data on table A1. Then the data that T1 has read is
different from table A1. Because T2 has updated the data on table A1. E.g. if T1 read a=1 and T2 updated a=2. Then a!=b.

Scenario 3. If T1 transaction reads data from table A1 with certain number of rows. If another concurrent transaction (T2) inserts more rows on table A1. The
number of rows read by T1 is different from rows on table A1.

Scenario 1 is called Dirty reads.

Scenario 2 is called Non-repeatable reads.

Scenario 3 is called Phantom reads.

So, isolation level is the extend to which Scenario 1, Scenario 2, Scenario 3 can be prevented.
You can obtain complete isolation level by implementing locking. That is preventing concurrent reads and writes to the same data from occurring. But it affects performance. The level of isolation depends upon application to application how much isolation is required.

ISOLATION_READ_UNCOMMITTED: Allows to read changes that haven’t yet been committed. It suffer from Scenario 1, Scenario 2, Scenario 3.

ISOLATION_READ_COMMITTED: Allows reads from concurrent transactions that have been committed. It may suffer from Scenario 2 and Scenario 3. Because other transactions may be updating the data.

ISOLATION_REPEATABLE_READ: Multiple reads of the same field will yield the same results untill it is changed by itself. It may suffer from Scenario 3. Because other transactions may be inserting the data.

ISOLATION_SERIALIZABLE: Scenario 1, Scenario 2, Scenario 3 never happen. It is complete isolation. It involves full locking. It affects performace because of locking.

You can test using:

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();
    }
   
}

You can debug and see the result with different values for isolation and propagation.

睫毛上残留的泪 2024-12-27 21:44:35

其他答案给出了有关每个参数的足够解释;但是,您要求提供一个真实世界的示例,以下示例阐明了不同传播选项的目的:

Suppose you're in charge of implementing a signup service in which a confirmation e-mail is sent to the user. You come up with two service objects, one for enrolling the user and one for sending e-mails, which the latter is called inside the first one. For example something like this:

/* 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)
 }
}

您可能已经注意到,第二个服务属于传播类型 REQUIRES_NEW,而且它很可能会引发异常(SMTP 服务器关闭、电子邮件无效或其他原因)。您可能不希望整个过程回滚,例如从数据库或其他内容中删除用户信息;因此,您可以在单独的事务中调用第二个服务。

Back to our example, this time you are concerned about the database security, so you define your DAO classes this way:

/* User DAO */
@Transactional(Propagation=MANDATORY)
class UserDAO{
 // some CRUD methods
}

这意味着每当创建 DAO 对象以及对数据库的潜在访问时,我们都需要确保该调用是从我们的一项服务内部进行的,这意味着应该存在实时事务;否则会出现异常。因此传播的类型是强制

Enough explanation about each parameter is given by other answers; However, you asked for a real-world example, here is the one that clarifies the purpose of different propagation options:


Suppose you're in charge of implementing a signup service in which a confirmation e-mail is sent to the user. You come up with two service objects, one for enrolling the user and one for sending e-mails, which the latter is called inside the first one. For example something like this:

/* 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)
 }
}

You may have noticed that the second service is of propagation type REQUIRES_NEW and moreover, chances are it throws an exception (SMTP server down , invalid e-mail, or other reasons). You probably don't want the whole process to roll back, like removing the user information from a database or other things; therefore you call the second service in a separate transaction.

Back to our example, this time you are concerned about the database security, so you define your DAO classes this way:

/* User DAO */
@Transactional(Propagation=MANDATORY)
class UserDAO{
 // some CRUD methods
}

Meaning that whenever a DAO object, and hence a potential access to DB, is created, we need to reassure that the call was made from inside one of our services, implying that a live transaction should exist; otherwise, an exception occurs. Therefore the propagation is of type MANDATORY.

洒一地阳光 2024-12-27 21:44:35

以下定义来自 byteslounge.com 教程 1 和 2

隔离级别定义一个事务对某些数据存储库所做的更改如何影响其他同时并发的事务
事务,以及更改后的数据如何以及何时变为
可用于其他交易。当我们定义一个事务时使用
Spring框架我们还可以配置隔离
将执行相同交易的级别。

@Transactional(isolation=Isolation.READ_COMMITTED)
公共无效 someTransactionalMethod(对象 obj){

}

READ_UNCOMMITTED 隔离级别表明事务可以读取
其他事务尚未提交的数据。

READ_COMMITTED 隔离级别表明事务无法读取
其他事务尚未提交的数据。

REPEATABLE_READ 隔离级别规定如果一个事务读取一个
从数据库中多次记录所有这些结果
读取操作必须始终相同。

SERIALIZABLE 隔离级别是所有隔离中限制最严格的
水平。事务在所有级别(读、
范围和写锁定),因此它们看起来就像是在一个
序列化方式。

传播是决定如何将业务方法封装在逻辑或物理事务中的能力。

Spring REQUIRED 行为意味着将使用相同的事务
如果当前bean方法中有一个已经打开的事务
执行上下文。

REQUIRES_NEW 行为意味着新的物理事务将
始终由容器创建。

NESTED 行为使得嵌套 Spring 事务使用相同的
物理事务,但在嵌套调用之间设置保存点,以便
内部事务也可以独立于外部事务回滚
交易。

强制行为规定现有已打开的事务必须
已经存在。如果不是,容器将抛出异常。

NEVER 行为表明现有已打开的事务不得
已经存在。如果事务存在,则会抛出异常
容器。

NOT_SUPPORTED 行为将在任何范围之外执行
交易。如果打开的交易已经存在,它将是
暂停。

SUPPORTS 行为将在事务范围内执行,如果
打开的交易已经存在。如果没有已经打开的
事务 该方法无论如何都会执行,但在非事务中
方式。

Following definitions are from byteslounge.com tutorials 1 and 2:

Isolation level defines how the changes made to some data repository by one transaction affect other simultaneous concurrent
transactions, and also how and when that changed data becomes
available to other transactions. When we define a transaction using
the Spring framework we are also able to configure in which isolation
level that same transaction will be executed.

@Transactional(isolation=Isolation.READ_COMMITTED)
public void someTransactionalMethod(Object obj) {

}

READ_UNCOMMITTED isolation level states that a transaction may read
data that is still uncommitted by other transactions.

READ_COMMITTED isolation level states that a transaction can't read
data that is not yet committed by other transactions.

REPEATABLE_READ isolation level states that if a transaction reads one
record from the database multiple times the result of all those
reading operations must always be the same.

SERIALIZABLE isolation level is the most restrictive of all isolation
levels. Transactions are executed with locking at all levels (read,
range and write locking) so they appear as if they were executed in a
serialized way.

Propagation is the ability to decide how the business methods should be encapsulated in both logical or physical transactions.

Spring REQUIRED behavior means that the same transaction will be used
if there is an already opened transaction in the current bean method
execution context.

REQUIRES_NEW behavior means that a new physical transaction will
always be created by the container.

The NESTED behavior makes nested Spring transactions to use the same
physical transaction but sets savepoints between nested invocations so
inner transactions may also rollback independently of outer
transactions.

The MANDATORY behavior states that an existing opened transaction must
already exist. If not an exception will be thrown by the container.

The NEVER behavior states that an existing opened transaction must not
already exist. If a transaction exists an exception will be thrown by
the container.

The NOT_SUPPORTED behavior will execute outside of the scope of any
transaction. If an opened transaction already exists it will be
paused.

The SUPPORTS behavior will execute in the scope of a transaction if an
opened transaction already exists. If there isn't an already opened
transaction the method will execute anyway but in a non-transactional
way.

荒人说梦 2024-12-27 21:44:35

事务代表数据库的一个工作单元。

事务传播:Spring 允许您指定事务如何在方法调用和嵌套事务之间传播。

事务隔离:Spring允许您指定事务的隔离级别,这样您就可以确保它们与其他并发事务隔离。

在 spring TransactionDefinition 接口中,定义了 Spring 兼容的事务属性。 @Transactional 注解描述了方法或类的事务属性。

@Autowired
private TestDAO testDAO;

@Transactional(propagation=TransactionDefinition.PROPAGATION_REQUIRED,isolation=TransactionDefinition.ISOLATION_READ_UNCOMMITTED)
public void someTransactionalMethod(User user) {

  // Interact with testDAO

}

传播(复制):用于事务间关系。 (类似于java线程间通信)

+-------+---------------------------+------------------------------------------------------------------------------------------------------+
| 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.                                 |
+-------+---------------------------+------------------------------------------------------------------------------------------------------+

隔离性:隔离性是数据库事务的 ACID(原子性、一致性、隔离性、持久性)属性之一。隔离性决定了事务完整性如何对其他用户和系统可见。它用于资源锁定,即并发控制,确保在给定点只有一个事务可以访问资源。

锁定感知:隔离级别决定了锁定的持续时间。

+---------------------------+-------------------+-------------+-------------+------------------------+
| Isolation Level Mode      |  Read             |   Insert    |   Update    |       Lock Scope       |
+---------------------------+-------------------+-------------+-------------+------------------------+
| READ_UNCOMMITTED (Low)    |  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 (High)       |   committed data  | Not Allowed | Not Allowed | Lock on full table     |
+---------------------------+-------------------+-------------+-------------+------------------------+

读感知:会出现以下3种主要问题:

  • 脏读:从另一个tx(事务)中读取未提交的数据。
  • 不可重复读取:从另一个交易读取已提交的UPDATES
  • 幻读:从另一个交易中读取提交的INSERTS和/或DELETES

下面的图表显示了哪种事务隔离级别解决了哪些并发问题:

+---------------------------+--------------+----------------------+----------------+
| Isolation Level Mode      |  Dirty reads | Non-repeatable reads | Phantoms reads |
+---------------------------+--------------+----------------------+----------------+
| READ_UNCOMMITTED          | X            | X                    | X           |
| READ_COMMITTED (Default)  | solves       | X                    | X           |
| REPEATABLE_READ           | solves       | solves               | X           |
| SERIALIZABLE              | solves       | solves               | solves      |
+---------------------------+--------------+----------------------+----------------+

示例

A Transaction represents a unit of work with a database.

Transaction propagation: Spring allows you to specify how transactions should propagate across method calls and nested transactions.

Transaction isolation: Spring allows you to specify the isolation level of your transactions, so you can ensure that they are isolated from other concurrent transactions.

In spring TransactionDefinition interface that defines Spring-compliant transaction properties. @Transactional annotation describes transaction attributes on a method or class.

@Autowired
private TestDAO testDAO;

@Transactional(propagation=TransactionDefinition.PROPAGATION_REQUIRED,isolation=TransactionDefinition.ISOLATION_READ_UNCOMMITTED)
public void someTransactionalMethod(User user) {

  // Interact with testDAO

}

Propagation (Reproduction) : is uses for inter transaction relation. (analogous to java inter thread 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 : Isolation is one of the ACID (Atomicity, Consistency, Isolation, Durability) properties of database transactions. Isolation determines how transaction integrity is visible to other users and systems. It uses for resource locking i.e. concurrency control, make sure that only one transaction can access the resource at a given point.

Locking perception: isolation level determines the duration that locks are held.

+---------------------------+-------------------+-------------+-------------+------------------------+
| Isolation Level Mode      |  Read             |   Insert    |   Update    |       Lock Scope       |
+---------------------------+-------------------+-------------+-------------+------------------------+
| READ_UNCOMMITTED (Low)    |  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 (High)       |   committed data  | Not Allowed | Not Allowed | Lock on full table     |
+---------------------------+-------------------+-------------+-------------+------------------------+

Read perception: the following 3 kinds of major problems occurs:

  • Dirty reads : reads uncommitted data from another tx(transaction).
  • Non-repeatable reads : reads committed UPDATES from another tx.
  • Phantom reads : reads committed INSERTS and/or DELETES from another tx

Below is a chart which shows which transaction isolation level solves which problems of concurrency:

+---------------------------+--------------+----------------------+----------------+
| Isolation Level Mode      |  Dirty reads | Non-repeatable reads | Phantoms reads |
+---------------------------+--------------+----------------------+----------------+
| READ_UNCOMMITTED          | X            | X                    | X           |
| READ_COMMITTED (Default)  | solves       | X                    | X           |
| REPEATABLE_READ           | solves       | solves               | X           |
| SERIALIZABLE              | solves       | solves               | solves      |
+---------------------------+--------------+----------------------+----------------+

for examples

空宴 2024-12-27 21:44:35

您几乎永远不想使用Read Uncommited,因为它并不真正符合ACIDRead Commmited 是一个很好的默认起点。 可重复读取可能只在报告、汇总或聚合场景中需要。请注意,许多数据库(包括 postgres)实际上并不支持可重复读取,您必须使用 Serialized 代替。 Serialized 对于您知道必须完全独立于其他任何事情发生的事情很有用;可以将其想象为 Java 中的同步。可序列化与 REQUIRES_NEW 传播密切相关。

我将 REQUIRES 用于运行 UPDATE 或 DELETE 查询以及“服务”级别函数的所有函数。对于仅运行 SELECT 的 DAO 级别函数,我使用 SUPPORTS ,如果该函数已经启动(即从服务函数调用),它将参与 TX。

You almost never want to use Read Uncommited since it's not really ACID compliant. Read Commmited is a good default starting place. Repeatable Read is probably only needed in reporting, rollup or aggregation scenarios. Note that many DBs, postgres included don't actually support Repeatable Read, you have to use Serializable instead. Serializable is useful for things that you know have to happen completely independently of anything else; think of it like synchronized in Java. Serializable goes hand in hand with REQUIRES_NEW propagation.

I use REQUIRES for all functions that run UPDATE or DELETE queries as well as "service" level functions. For DAO level functions that only run SELECTs, I use SUPPORTS which will participate in a TX if one is already started (i.e. being called from a service function).

奶茶白久 2024-12-27 21:44:35

事务隔离和事务传播虽然相关,但显然是两个截然不同的概念。在这两种情况下,默认值都是通过使用 声明式事务管理程序化交易管理。每个隔离级别和传播属性的详细信息可以在下面的参考链接中找到。

事务隔离

对于给定的两个或多个正在运行的事务/与数据库的连接,一个事务中的查询所做的更改如何以及何时对另一事务中的查询产生影响/可见。它还涉及使用哪种数据库记录锁定来将该事务中的更改与其他事务隔离开来,反之亦然。这通常由参与事务的数据库/资源​​来实现。

事务传播

在企业应用程序中针对任何给定的请求/处理过程涉及许多组件来完成工作。其中一些组件标记了将在相应组件及其子组件中使用的事务的边界(开始/结束)。对于组件的事务边界,事务传播指定相应组件是否将参与事务,以及如果调用组件已经或没有已创建/启动的事务,会发生什么情况。这与 Java EE 事务属性相同。这通常由客户端事务/连接管理器实现。

参考:

Transaction Isolation and Transaction Propagation although related but are clearly two very different concepts. In both cases defaults are customized at client boundary component either by using Declarative transaction management or Programmatic transaction management. Details of each isolation levels and propagation attributes can be found in reference links below.

Transaction Isolation

For given two or more running transactions/connections to a database, how and when are changes made by queries in one transaction impact/visible to the queries in a different transaction. It also related to what kind of database record locking will be used to isolate changes in this transaction from other transactions and vice versa. This is typically implemented by database/resource that is participating in transaction.

.

Transaction Propagation

In an enterprise application for any given request/processing there are many components that are involved to get the job done. Some of this components mark the boundaries (start/end) of a transaction that will be used in respective component and it's sub components. For this transactional boundary of components, Transaction Propogation specifies if respective component will or will not participate in transaction and what happens if calling component already has or does not have a transaction already created/started. This is same as Java EE Transaction Attributes. This is typically implemented by the client transaction/connection manager.

Reference:

等风来 2024-12-27 21:44:35

我已经使用不同的传播模式运行了 outerMethodmethod_1method_2

以下是不同传播模式的输出。

外部方法

    @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 - 无事务
    • 方法_1 - 传播。强制)-
    • Method_2 - 仅交易注释
    • 输出:method_1 将抛出没有现有事务的异常

    • OuterMethod - 无事务
    • Method_1 - 仅交易注释
    • 方法_2 - 传播。强制)
    • 输出:method_2 将抛出没有现有事务的异常
    • 输出:method_1 将在数据库中保留记录。

    • OuterMethod - 带事务
    • Method_1 - 仅交易注释
    • 方法_2 - 传播。强制)
    • 输出:method_2 将在数据库中保留记录。
    • 输出:method_1 将在数据库中保留记录。
      -- 这里主要外部现有事务用于方法 1 和 2

    • OuterMethod - 带事务
    • 方法_1 - 传播。强制)
    • Method_2 - 仅事务注释并引发异常
    • 输出:数据库中没有记录持久意味着回滚已完成。

    • OuterMethod - 带事务
    • 方法_1 - 传播。REQUIRES_NEW)
    • Method_2 - Propagation.REQUIRES_NEW) 并引发 1/0 异常
    • 输出:method_2 将引发异常,因此 method_2 记录不会保留。
    • 输出:method_1 将在数据库中保留记录。
    • 输出:method_1 没有回滚

I have run outerMethod, method_1 and method_2 with different propagation modes.

Below is the output for different propagation modes.

Outer Method

    @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.MANDATORY) -
    • Method_2 - Transaction annotation only
    • Output: method_1 will throw exception that no existing transaction

    • OuterMethod - Without transaction
    • Method_1 - Transaction annotation only
    • Method_2 - Propagation.MANDATORY)
    • Output: method_2 will throw exception that no existing transaction
    • Output: method_1 will persist record in database.

    • OuterMethod - With transaction
    • Method_1 - Transaction annotation only
    • Method_2 - Propagation.MANDATORY)
    • Output: method_2 will persist record in database.
    • Output: method_1 will persist record in database.
      -- Here Main Outer existing transaction used for both method 1 and 2

    • OuterMethod - With transaction
    • Method_1 - Propagation.MANDATORY)
    • Method_2 - Transaction annotation only and throws exception
    • Output: no record persist in database means rollback done.

    • OuterMethod - With transaction
    • Method_1 - Propagation.REQUIRES_NEW)
    • Method_2 - Propagation.REQUIRES_NEW) and throws 1/0 exception
    • Output: method_2 will throws exception so method_2 record not persisted.
    • Output: method_1 will persist record in database.
    • Output: There is no rollback for method_1
晌融 2024-12-27 21:44:35

我们可以为此添加:

@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
    }
}

We can add for this:

@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
    }
}
水晶透心 2024-12-27 21:44:35

你可以这样使用:

@Transactional(propagation = Propagation.REQUIRES_NEW)
public EventMessage<ModificaOperativitaRapporto> activate(EventMessage<ModificaOperativitaRapporto> eventMessage) {
//here some transaction related code
}

你也可以使用这个东西:

public interface TransactionStatus extends SavepointManager {
    boolean isNewTransaction();
    boolean hasSavepoint();
    void setRollbackOnly();
    boolean isRollbackOnly();
    void flush();
    boolean isCompleted();
}

You can use like this:

@Transactional(propagation = Propagation.REQUIRES_NEW)
public EventMessage<ModificaOperativitaRapporto> activate(EventMessage<ModificaOperativitaRapporto> eventMessage) {
//here some transaction related code
}

You can use this thing also:

public interface TransactionStatus extends SavepointManager {
    boolean isNewTransaction();
    boolean hasSavepoint();
    void setRollbackOnly();
    boolean isRollbackOnly();
    void flush();
    boolean isCompleted();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文