One, foreword

Because recently always is such and such things bother, no continue to write articles, has been vacant for two weeks, although the work does not need knowledge of computer programming, but I will always stick to it, and not because of carefree boring, but because of love, while writing code sucks, knowledge is not enough, I will always insist, every uploaded to the nuggets, Seeing some people “like” me, I have a lot of motivation in my heart. I also often encounter some troubles in my work, but I believe that as long as I stick to it and do myself well, I will definitely go further.

What is transaction management?

1, concept,

Baidu Encyclopedia definition

It’s usually something to do or do. In computer terms, a unit of program execution that accesses and possibly updates various data items in a database. Transactions are typically caused by the execution of a user program written in a high-level database manipulation language or programming language (such as SQL, C++, or Java) and defined by statements (or function calls) in the form begin transaction and end transaction. A transaction consists of all the operations performed between a begin transaction and an end Transaction.

understand

In order to achieve a certain goal, and a series of operations, these operations or success together, with either fail, update the data in the database, for example, if there are any abnormal, we hope don’t update the data, the abnormal process, database updates should also fail, if the program does not have any problems (abnormal), normal program is running, We hope the database will be updated as well.

Example: IF I go to the ATM of the bank to withdraw money, if the money is deducted from my account, but the ATM does not come out, then I will lose money. If the deduction fails, but the MONEY comes out of the ATM, then the bank will lose money; What we hope is that no matter which side has a problem, all operations will be cancelled (i.e., no money will be deducted and no money will come out of the ATM), which means the whole process of withdrawing money will be rolled back and no loss will be caused to either sideCopy the code
Example: Xiao Ming met a girlfriend, from fall in love to get married, when ready to get married, the girlfriend asked him to pursue again, this time is rollbackCopy the code

2. Transaction characteristics

  • Atomicity: Once a transaction completes (whether it succeeds or fails), the system must ensure that the business it models is in a consistent state, not partially complete, partially failed, partially successful, and partially failed.
  • Consistency: A transaction is an atomic operation, consisting of a series of actions. The atomicity of the transaction ensures that the action either completes or does not work at all. In the case of a transfer, the sum of the money in the two accounts does not change whether the transaction is committed or not
Xiao Ming has1000Yuan, floret has2000Yuan sum to3000Yuan Xiaoming ------- transfer500Yuan ---------> Floret Xiao Ming has this time500yuan Flowers have2500Yuan, the sum is3000yuanCopy the code
  • Isolation: There may be many transactions processing the same data at the same time, so each transaction should be isolated from others to prevent data corruption (Isolation level).
  • Durability: Once transactions complete, whatever system errors occur, their results should not be affected so they can recover from any system crash. Typically, the result of a transaction is written to a persistent store.

3. Isolation level

** Isolation levels in ascending order are: **ISOLATION_READ_UNCOMMITTED, ISOLATION_READ_COMMITTED, ISOLATION_REPEATABLE_READ, and ISOLATION_SERIALIZABLE

3.1 Dirty read (read an uncommitted new thing and then rolled back)

A: update A= 12, uncommitted, encounter rollback, A=0; Select A ------> 12 from A where B =0; select A ------> 12 from A where B =0;Copy the code

3.2 Non-repeatable read (read the new thing submitted, refers to the update operation)

Within the same transaction, the results of multiple queries are different, and during the interval of the query, another transaction modifies the data and commits

Note: Both queries in transaction A are in the same transaction

A = 1000,B = 2000A: select A ---------->1000
       A + B = 3000Transaction B: update A =2000Transaction A(within the same transaction as first select operation) : select A ------------>2000
      A + B = 4000


Copy the code

3.3 Magic read (read the submitted new thing, as long as it is for add or delete operation)

A = [1.2.3]; Transaction A: read A-------->[1.2.3] Transaction B: new data [4]----> A = [1.2.3.4A--------> A = [1.2.3.4]
Copy the code

During multiple reads of transaction A, transaction B performs new operations on the data, resulting in different results for multiple reads of transaction A

3.4 Transaction Loss

Transaction A and transaction B execute A data at the same time, then transaction B commits, but transaction A is rolled back, so transaction B is lost due to the rollback of transaction A

3.5 Transaction Coverage

As shown in the figure, Xiaohua and Xiaoming are performing a data operation at the same time. Xiaohua’s transaction has been committed (50 yuan is saved), and Xiaoming is committed later, resulting in xiaohua’s transaction being overwritten ** (Key points: Because is performed at the same time, so they will store the beginning balance snapshot < 100 >, floret in 50 yuan, the balance to 150 yuan, xiao Ming is deposited in the 50 yuan, normally should be 200 yuan, but xiao Ming’s snapshot transaction store balance is 100 yuan, so after the transaction is committed, the balance is 150 yuan, lost the flower of 50 yuan) * *

4. Isolation level

  • ISOLATION_DEFAULT: Use the default isolation level of the back-end database
  • ISOLATION_READ_UNCOMMITTED: the lowest isolation level that allows uncommitted data changes to be read, which may result in dirty, phantom, or unrepeatable reads
  • ISOLATION_READ_COMMITTED: Allows reading of data that has already been committed by concurrent transactions. Dirty reads are prevented, but phantom or non-repeatable reads may still occur
  • ISOLATION_REPEATABLE_READ: The result of multiple reads of the same field is consistent unless the data is modified by the transaction itself. This prevents dirty reads and non-repeatable reads, but magic reads can still occur, i.e. when a transaction starts reading data (transaction is open), no modification is allowed
  • ISOLATION_SERIALIZABLE: Highest isolation level, fully compliant with ACID isolation level, ensuring that dirty reads, non-repeatable reads, and phantom reads are blocked, and slowest transaction isolation level ** (less efficient) ** because it is usually achieved by fully locking the database tables related to the transaction
Isolation level Dirty read Unrepeatable read Phantom read
ISOLATION_READ_UNCOMMITTED may may may
ISOLATION_READ_COMMITTED Can’t be may may
ISOLATION_REPEATABLE_READ (repeatable read) Can’t be Can’t be may
ISOLATION_SERIALIZABLE Can’t be Can’t be Can’t be

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
    @AliasFor("transactionManager")
    String value(a) default "";

    @AliasFor("value")
    String transactionManager(a) default "";

    String[] label() default {};

  	// Propagation characteristics
    Propagation propagation(a) default Propagation.REQUIRED;

  	// Isolation level
    Isolation isolation(a) default Isolation.DEFAULT;

  	The default value is -1. There is no timeout period
    int timeout(a) default- 1;

    String timeoutString(a) default "";

    boolean readOnly(a) default false;

    // The exception that needs to be rolled back. If the attribute is set, the specified exception will be rolled back
    Class<? extends Throwable>[] rollbackFor() default {};

    String[] rollbackForClassName() default {};

  	// If this property is set, the specified exception will not be rolled back
    Class<? extends Throwable>[] noRollbackFor() default {};

    String[] noRollbackForClassName() default {};
}
Copy the code

The generic Dao class

@Repository
public class UserDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    /** * Query data *@param id
     */
    public void query(int id){
        String sql = "select * from user where id ="+id;
        System.out.println(jdbcTemplate.queryForMap(sql));
    }

    /** * Update name *@param id
     * @return* /
    public boolean updateName(int id){
        String sql = "Update user set name = '1' where id ="+id;
        jdbcTemplate.update(sql);
        return true;
    }

    /** * Update amount *@param id
     * @return* /
    public boolean updateMoney(int id){
        String sql = "update user set money = money + 10 where id = "+id;
        jdbcTemplate.execute(sql);
        return false; }}Copy the code

4.1 ISOLATION_READ_UNCOMMITTED

Lowest isolation level that allows reading of uncommitted data changes,Dirty read, phantom read, or unrepeatable read may occur

@Test
public void testRuIsolation(a) throws InterruptedException {
   int id = 1;
   new Thread(()->{
      try {
         userService.ruTransaction(id);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
   }).start();
   userService.readRuTranscation(id);

}
Copy the code

Unrepeatable read

Within the same transaction, the results of multiple queries are different, and during the interval of the query, another transaction modifies the data and commits

/** * Specifies the isolation level of ru. Unrepeatable reads, magic reads, dirty reads * occur@return* /
@Transactional(isolation = Isolation.READ_UNCOMMITTED,rollbackFor = Exception.class)
public boolean ruTransaction(int id) throws InterruptedException {
    userDao.updateMoney(id);
    //4. Read the submitted data
    userDao.query("Transaction A",id);

    return false;
}


/ * * * *@param id
 * @throws InterruptedException
 */
@Transactional(isolation = Isolation.READ_UNCOMMITTED,readOnly = true,rollbackFor = Exception.class)
public void readRuTranscation(int id) throws InterruptedException {
    userDao.query("Transaction B",id);
    Thread.sleep(1000);
    userDao.query("Transaction B",id);
}
Copy the code
A{name= xiao Ming, money=1765, id=1} transaction B{name= xiaoming, money=1755, id=1} transaction B{name= xiaoming, money=1765, id=1}
Copy the code

As you can see from the output, transaction B does not read the same money twice, so there is a non-repeatable read problem

Dirty read

A{name= xiao Ming, money=1765, id=1} transaction B{name= xiaoming, money=1755, id=1}
Copy the code

Here transaction B reads the uncommitted transaction of transaction A, when the uncommitted transaction is 1755

Phantom read

/** * Specifies the isolation level of ru. Unrepeatable reads, magic reads, dirty reads * occur@return* /
@Transactional(isolation = Isolation.READ_UNCOMMITTED,rollbackFor = Exception.class)
public boolean ruTransaction(int id) throws InterruptedException {
    // Update all money to 1005
    userDao.updateMoney();
    // Read the updated data
    System.out.println("Business A:"+  userDao.queryList());
    return false;
}


/ * * * *@param id
 * @throws InterruptedException
 */
@Transactional(isolation = Isolation.READ_UNCOMMITTED,rollbackFor = Exception.class)
public void readRuTranscation(int id) throws InterruptedException {
  	// Insert new data
    this.insert(3."yangzinan".1000);
    Thread.sleep(1000);
}
Copy the code

Call:

@Test
public void testRuIsolation(a) throws InterruptedException {
   int id = 1;
   new Thread(()->{
      try {
         userService.ruTransaction(id);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
   }).start();
   userService.readRuTranscation(id);

}
Copy the code

Results:

[User{id=1, name='Ming', money=1005}, User{id=2, name='flower', money=1005}, User{id=3, name='yangzinan', money=1000}]
Copy the code

Transaction A modifies all data and changes all money to 1005, but at this time, transaction B adds A new line of data. When transaction A reads again, it finds that there is no record of modification, and then magic read appears

4.2 ISOLATION_READ_COMMITTED

Allowing data that has already been committed by concurrent transactions prevents dirty reads, butPhantom or non-repeatable readIt could still happen

Dirty read and non-repeatable read

@Transactional(isolation = Isolation.READ_COMMITTED,rollbackFor = Exception.class)
public boolean rcTranscation(int id) throws InterruptedException {
    userDao.updateMoney(id);
    userDao.query("Transaction A",id);
    return true;
}

@Transactional(isolation = Isolation.READ_COMMITTED,readOnly = true,rollbackFor = Exception.class)
public boolean readRcTranscation(int id) throws InterruptedException {
    userDao.query("Transaction B",id);
    Thread.sleep(1000);
    userDao.query("Transaction B",id);
    return true;
}
Copy the code
@Test
public void testRcIsolation(a) throws InterruptedException {
   int id = 1;
   new Thread(()->{
      try {
         userService.rcTranscation(id);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
   }).start();
   userService.readRcTranscation(id);
}
Copy the code
Transaction B{name= xiaoming, money=1035, id=1} transaction A{name= xiaoming, money=1045, id=1} transaction B{name= xiaoming, money=1045, id=1}
Copy the code

While dirty reads are avoided above, non-repeatable reads also appear

Phantom read

4.3 ISOLATION_REPEATABLE_READ

Multiple reads of the same field are consistent unless the data is modified by the transaction itself, which can prevent dirty reads and non-repeatable reads, butPhantom readIt is also possible that when a transaction begins to read data (transaction start), no modification operations are allowed

Dirty read and non-repeatable read

Transaction B{name= xiaoming, money=1045, id=1} transaction A{name= xiaoming, money=1055, id=1} transaction B{name= xiaoming, money=1045, id=1}
Copy the code

Transaction B reads the same data both times, so it solves the non-repeatable read problem

4.4 ISOLATION_SERIALIZABLE

Highest isolation level, fully compliant to ACID isolation level,Ensure that dirty reads, non-repeatable reads, and phantom reads are prevented, is also the slowest transaction isolation level ** (less efficient) ** because it is usually achieved by fully locking the database tables associated with the transaction

Dirty read

@Transactional(isolation = Isolation.SERIALIZABLE)
public boolean seTranscation(int id){
    userDao.updateMoney(id);
    userDao.query("Transaction A",id);
    return true;
}

@Transactional(isolation = Isolation.SERIALIZABLE,readOnly = true)
public boolean readSeTranscation(int id) throws InterruptedException {
    userDao.query("Transaction B",id);
    Thread.sleep(1000);
    userDao.query("Transaction B",id);
    return true;
}
Copy the code
@Test
public void testSeIsolation(a) throws InterruptedException {
   int id = 1;
   new Thread(()->{
      userService.seTranscation(id);
   }).start();
   userService.readSeTranscation(id);
}
Copy the code
@Test
public void testRcIsolation(a) throws InterruptedException {
   int id = 1;
   new Thread(()->{
      userService.rcTRanscation(id);
   }).start();
   userService.readRcTranscation(id);

}
Copy the code

Unrepeatable read

@Transactional(isolation = Isolation.SERIALIZABLE)
public boolean seTranscation(int id){
    userDao.updateMoney(id);
    userDao.query("Transaction A",id);
    return true;
}

@Transactional(isolation = Isolation.SERIALIZABLE,readOnly = true)
public boolean readSeTranscation(int id) throws InterruptedException {
    userDao.query("Transaction B",id);
    Thread.sleep(1000);
    userDao.query("Transaction B",id);
    return true;
}
Copy the code
@Test
public void testSeIsolation(a) throws InterruptedException {
   int id = 1;
   new Thread(()->{
     // Update the data
      userService.seTranscation(id);
   }).start();
  // Get the data
   userService.readSeTranscation(id);

}
Copy the code
A{name= xiao Ming, money=1065, id=1} transaction B{name= xiaoming, money=1065, id=1} transaction B{name= xiaoming, money=1065, id=1}
Copy the code

Phantom read

@Transactional(isolation = Isolation.SERIALIZABLE,rollbackFor = Exception.class)
public boolean seTransaction(a) throws InterruptedException {
    // Update all money to 1005
    userDao.updateMoney();
    // Read the updated data
    System.out.println("Business A:"+  userDao.queryList());
    return false;
}


@Transactional(isolation = Isolation.SERIALIZABLE,rollbackFor = Exception.class)
public void readSeTranscation(a) throws InterruptedException {
    this.insert(3."yangzinan".1000);
    Thread.sleep(1000);
}
Copy the code
@Test
public void seTranscation(a) throws InterruptedException {
   new Thread(()->{
      try {
         userService.ruTransaction();
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
   }).start();
   userService.readRuTranscation();
}
Copy the code
[User{id=1, name='Ming', money=1005}, User{id=2, name='flower', money=1005}, User{id=3, name='yangzinan', money=1005}, User{id=4, name='yangzinan', money=1005}]
Copy the code

Transaction A updates all data (money=1005), transaction B adds data (id=4), transaction B changes money to 1005, so no magical reading occurs

5. Transmission behavior

When a transaction method is called by another transaction method, what should the transaction method do

PROPAGATION_REQUIRED If there is no current transaction, create a new transaction, and if there is one, join it, this is the most common choice.
PROPAGATION_SUPPORTS Supports the current transaction, and executes in a non-transactional way if there is no current transaction.
PROPAGATION_MANDATORY Use the current transaction, and throw an exception if there is no current transaction.
PROPAGATION_REQUIRES_NEW Create a new transaction. If there is one, suspend the current transaction and create a new one
PROPAGATION_NOT_SUPPORTED Performs operations in a non-transactional manner and suspends the current transaction if one exists.
PROPAGATION_NEVER Performs the operation in a non-transactional manner, throwing an exception if the current transaction exists.
PROPAGATION_NESTED Executes within a nested transaction, if one currently exists. If there is no current transaction, an operation similar to PROPAGATION_REQUIRED is performed

Here we first test what happens when you are not running in a transaction:

public void xiaoming(a){
    // Update xiao Ming's amount
    userDao.updateMoney(1);

    // Update floret name to floret update
    this.xiaohua();

    int i = 10;
    if(i == 10) {throw new RuntimeException("Something went wrong."); }}public void xiaohua(a){
    userDao.updateName(2);
}
Copy the code

Although the exception is thrown, the money of Xiao Ming in the database is +10 on the original basis, and the name of Xiaohua is changed to Xiaohua Update

PROPAGATION_REQUIRED: Create a new transaction if no transaction exists, and join one if one already exists. This is the most common option.

public void xiaoming(a){
    // Update xiao Ming's amount
    userDao.updateMoney(1);

    try {
        // Update floret name to floret update
        userService.xiaohua();
    }catch (RuntimeException e){
        e.printStackTrace();
    }
    int i = 9;
    if(i == 10) {throw new RuntimeException("Something went wrong."); }}@Transactional(propagation = Propagation.REQUIRED)
public void xiaohua(a){
    userDao.updateName(2);
  	// Throw an exception
    int a = 10;
    if(a == 10) {throw new RuntimeException("Xiaohua made a mistake."); }}Copy the code

Because xiaoming is not managed by transactions, when xiaohua is called, it adds a new transaction to xiaohua’s method, so when Xiaohua throws an exception, it only rolls back Xiaohua’s method.

    @Transactional
    public void xiaoming(a){
        // Update xiao Ming's amount
        userDao.updateMoney(1);

        try {
            // Update floret name to floret update
            userService.xiaohua();
        }catch (RuntimeException e){
            e.printStackTrace();
        }
        int i = 9;
        if(i == 10) {throw new RuntimeException("Something went wrong."); }}@Transactional(propagation = Propagation.REQUIRED)
    public void xiaohua(a){
        userDao.updateName(2);
     		 // Throw an exception
        int a = 10;
        if(a == 10) {throw new RuntimeException("Xiaohua made a mistake."); }}Copy the code

In the example above, xiaoming uses transaction management (@transactional), so Xiaohua also uses transaction management. Xiaohua throws an exception, which also causes Xiaoming to roll back.

** supports the current transaction, if there is no current transaction, execute with a non-transactional method.

@Transactional
public void xiaoming(a){
    // Update xiao Ming's amount
    userDao.updateMoney(1);

    // Update floret name to floret update
    userService.xiaohua();

    int i = 9;
    if(i == 10) {throw new RuntimeException("Something went wrong."); }}@Transactional(propagation = Propagation.SUPPORTS)
public void xiaohua(a){
    userDao.updateName(2);
    int a = 10;
    if(a == 10) {throw new RuntimeException("Xiaohua made a mistake."); }}Copy the code

In the example above, Xiaohua’s method fails, and Xiaohua rolls back. It shows that xiaoming’s transaction method and Xiaohua’s transaction method are in the same transaction

public void xiaoming(a){
    // Update xiao Ming's amount
    userDao.updateMoney(1);

    // Update floret name to floret update
    userService.xiaohua();

    int i = 9;
    if(i == 10) {throw new RuntimeException("Something went wrong."); }}@Transactional(propagation = Propagation.SUPPORTS)
public void xiaohua(a){
    userDao.updateName(2);
    int a = 10;
    if(a == 10) {throw new RuntimeException("Xiaohua made a mistake."); }}Copy the code

Xiaoming removes transactions, so Xiaohua is not managed by transactions, and there is no rollback when an error occurs.

**PROPAGATION_MANDATORY:** Use the current transaction, if no current transaction exists, throw an exception.

public void xiaoming(a){
    // Update xiao Ming's amount
    userDao.updateMoney(1);
    try {
        // Update floret name to floret update
        userService.xiaohua();
    }catch (RuntimeException e){
        e.printStackTrace();
    }

    int i = 9;
    if(i == 10) {throw new RuntimeException("Something went wrong."); }}@Transactional(propagation = Propagation.MANDATORY)
public void xiaohua(a){
    userDao.updateName(2);
    int a = 10;
    if(a == 10) {throw new RuntimeException("Xiaohua made a mistake."); }}Copy the code

There is no transaction added in the Xiaoming method, so an exception is thrown

org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
Copy the code

If Xiaoming is managed by a transaction, xiaohua will also be added to the transaction execution.

** execute a non-transactional operation, throw an exception if the current transaction exists.

@Transactional
public void xiaoming(a){
    // Update xiao Ming's amount
    userDao.updateMoney(1);

    // Update floret name to floret update
    userService.xiaohua();

    int i = 10;
    if(i == 10) {throw new RuntimeException("Something went wrong."); }}/** * does not run in a transaction environment, if there is a transaction, throw an exception */
@Transactional(propagation = Propagation.NEVER)
public void xiaohua(a){
    userDao.updateName(2);
}
Copy the code
org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'
Copy the code

In the example above, we are using NERVER’s propagation feature on xiaohua’s method. Therefore, when xiaoming is called, it is not allowed to have transactions. If there are transactions in xiaoming, we will throw the above exception

** execute a non-transactional operation, suspending the current transaction if one exists.

@Transactional(propagation = Propagation.REQUIRED)
public void xiaoming(a){
    // Update xiao Ming's amount
    userDao.updateMoney(1);

    // Update floret name to floret update
    userService.xiaohua();

    int i = 10;
    if(i == 10) {throw new RuntimeException("Something went wrong."); }}/** * If there is a transaction, the transaction will be suspended and a deadlock will occur */
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void xiaohua(a){
    userDao.updateName(2);
}
Copy the code

In the example above, because Xiaohua is using NOT_SUPPORTED, xiaoming’s transaction method is suspended, causing a deadlock, and an exception is thrown when the timeout is reached

**PROPAGATION_REQUIRES_NEW: ** Create a new transaction, suspend the current transaction, and create a new transaction

 @Transactional(propagation = Propagation.REQUIRED)
    public void xiaoming(a){
        // Update xiao Ming's amount
        userDao.updateMoney(1);

        try {
            // Update floret name to floret update
            userService.xiaohua();
        }catch (RuntimeException e){
            e.printStackTrace();
        }

        int i = 9;
        if(i == 10) {throw new RuntimeException("Something went wrong."); }}/** * Create a new transaction. If a transaction exists, suspend it and create a new transaction */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void xiaohua(a){
        userDao.updateName(2);
        int a = 10;
        if(a == 10) {throw new RuntimeException("Xiaohua made a mistake."); }}Copy the code

In the example above, xiaoming has no transactions. Xiaohua assigns REQUIRES_NEW, and xiaohua creates a new transaction

Xiaohua rolling back doesn’t affect Xiaoming, and xiaoming rolling back doesn’t affect Xiaohua

** Execute within a nested transaction, if any existing transaction exists. If there is no current transaction, an operation similar to PROPAGATION_REQUIRED is performed

If Xiaohua throws an exception, xiaoming will also roll back

@Transactional(propagation = Propagation.REQUIRED)
public void xiaoming(a){
    // Update xiao Ming's amount
    userDao.updateMoney(1);
    try {
        // Update floret name to floret update
        userService.xiaohua();
    }catch (RuntimeException e){
        e.printStackTrace();
    }
    int i = 10;
    if(i == 10) {throw new RuntimeException("Something went wrong."); }}@Transactional(propagation = Propagation.NESTED)
public void xiaohua(a){
    userDao.updateName(2);
    int a = 9;
    if(a == 10) {throw new RuntimeException("Xiaohua made a mistake."); }}Copy the code

3. Two ways to define transactions (see the Application Content case)

1. Programmatic transactions

Declarative transactions

Fourth, using

1. Define the data table

2. Entity class

public class User {
    private String name;
    private Integer money;

    public String getName(a) {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getMoney(a) {
        return money;
    }

    public void setMoney(Integer money) {
        this.money = money;
    }

    @Override
    public String toString(a) {
        return "User{" +
                "name='" + name + '\' ' +
                ", money=" + money +
                '} '; }}Copy the code

3. Persistence layer

@Repository
public class UserDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;


    /** * Roll out *@paramFormName who transfers money *@paramHow much money */
    public void out(String formName,Integer money){
        String sql = "update user set money = money - ? where name = ?";
        jdbcTemplate.update(sql,money,formName);
    }

    /**
     * 转入
     * @paramToName to whom *@paramHow much money */
    public void in(String toName,Integer money){
        String sql = "update user set money = money + ? and where name = ?"; jdbcTemplate.update(sql,money,toName); }}Copy the code

4. Service layer

@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    /** * Transfer operation *@param fromName
     * @param toName
     * @param money
     */
    public void transfet(String fromName,String toName,Integer money){
        / / money
        userDao.out(fromName,money);
        int x = 10;
        /** * Simulation exception */
        if(x == 10) {throw new RuntimeException("Error");
        }
        / / turn to moneyuserDao.in(toName,money); }}Copy the code

Test 1: The two operations of the transfer are not in the same transaction. An exception is thrown after the money is transferred

Floret:1000Yuan xiao Ming:1000Yuan Xiaoming transfers to Floret20yuanCopy the code
@Autowired
private UserService userService;


@Test
void contextLoads(a) {
   // Transfer operation
   userService.transfet("Xiao Ming"."Flower".20);
}
Copy the code

Because we have set it so that if the amount exceeds 10 yuan, an exception will be thrown, so we choose to transfer 20 yuan and throw an exception

Java. Lang. RuntimeException: errorCopy the code

Because the first step we do is to deduct 20 yuan of Xiao Ming, and then increase 20 yuan of Xiao Ming. Normally, the balance should be xiao Ming: 980, and Xiao Ming: 1020. But because we throw an exception before increasing the amount of xiao Ming, the amount of Xiao Ming does not increase, but the amount of Xiao Ming decreases

 				/ / money
        userDao.out(fromName,money);
        int x = 10;
        /** * Simulation exception */
        if(x == 10) {throw new RuntimeException("Error");
        }
        / / turn to money
        userDao.in(toName,money);
Copy the code

Test 2: Add transaction support – programmatic transactions

Add transactions in the service layer

@Autowired
private UserDao userDao;

@Autowired
private DataSourceTransactionManager dataSourceTransactionManager;

@Autowired
private TransactionTemplate transactionTemplate;

/** * Transfer operation *@param fromName
 * @param toName
 * @param money
 */
public void transfet(String fromName,String toName,Integer money){

  	// Leave it to the transaction manager
    transactionTemplate.execute(status ->{
        / / money
        userDao.out(fromName,money);
        int x = 10;
        /** * Simulation exception */
        if(x == 10) {throw new RuntimeException("Error");
        }
        / / turn to money
        userDao.in(toName,money);

        return null;
    });

}
Copy the code

Even if an error occurs, the data in the database will not change, and the rollback is performed

Test 3: Add transaction support – declarative transactions

Add the ** @transactional ** annotation to the service layer to indicate that the method is managed by transactions

  /** * Transfer operation *@param fromName
     * @param toName
     * @param money
     */
    @Transactional
    public void transfet(String fromName,String toName,Integer money){

            / / money
            userDao.out(fromName,money);

            log.info("{} transfers {} yuan to {}",fromName,toName,money);

            int x = 10;
            /** * Simulation exception */
            if(x == 10) {throw new RuntimeException("Error");
            }
            / / turn to money
            userDao.in(toName,money);

    }
Copy the code

Even if an error occurs, the data in the database will not change, and the rollback is performed