Spring transaction management

I Spring transaction management

  1. What is a transaction

    Transaction is a unit of concurrency control, which enables a user-defined sequence of operations. These transactions are either done or not done. They are an inseparable work unit. Through transactions, sql can bind a set of logically related operations together, so that the server can maintain the complete row of data. Transactions usually start with begin/start trsaction and end with commit or rollback. Commit refers to all operations that commit a transaction. Specifically, all data updates in the transaction are written to the physical database on the disk, and the transaction ends normally. Rollback means rollback, that is, if a fault occurs during the operation of the transaction and the transaction cannot continue, the system will undo all completed operations on the database in the transaction and roll to the start state of the transaction.

    For example, for an online shopping transaction, the regular payment process includes at least the following database operations:

    • Update the inventory information of goods purchased by customers
    • Save customer payment information - may include interaction with the banking system
    • Generate the order and save it to the database
  2. Characteristics of the transaction (ACID)

    • Atomicity

      Transaction is the logical work unit of the database, and it must be an atomic work unit. For the modification of its data, either all or no execution is performed

    • uniformity

      When a transaction is completed, everything must be in a consistent state. In the relevant database, all rules must be applied to the modification of transactions to maintain the integrity of all things.

    • Isolation

      The execution of a transaction cannot be affected by other transactions. The enterprise database can handle thousands of concurrent accesses every second, which brings the problem of concurrency control.

    • persistence

      Once a transaction is committed, the transaction operation will be permanently saved to the DB. Even if the rollback operation is performed at this time, the changes can not be undone

  3. Concurrency of transactions

    • Dirty read

      A transaction reads the result of a data operation that is not committed by another transaction. This is quite dangerous because it is likely that all operations will be rolled back

    • NonRepeatable Read

      A transaction reads the same record twice, but gets different results. For example, after transaction T1 reads a certain data, transaction T2 modifies it. When transaction T1 reads the transaction again, the value is different from the previous time

    • Phantom Read

      The transaction performs two queries (table operations) during the operation. The results of the second query include the data that does not appear in the first query or the data that appears in the first query is missing. This is because another transaction inserts data during the two queries

  4. Isolation level of transaction

    • Read uncommitted

      Read uncommitted: the lowest level. None of the above conditions can be guaranteed

    • Read one submit

      Read committed: to avoid dirty reading

    • Repeatable reading

      Repeatable read: it can avoid dirty reading and non repeatable reading. Unreal reading cannot be avoided

    • Serial read

      Serializable: transactions can only be executed one by one, avoiding dirty reading, non repeatable reading and phantom reading. Slow execution efficiency, use with caution.

  5. Propagation behavior of transactions

    The first aspect of a transaction is propagation behavior. When a transaction method is called by another transaction, you must formulate how the transaction should be propagated. For example, a method may continue to run in an existing transaction, or it may start a new transaction and run in its own transaction. spring defines the propagation behavior in 7:

    Communication behavior

    meaning

    PROPAGATION_REQUIRED

    Indicates that the current method must run in a transaction. If the current transaction exists, the method will run in that transaction. Otherwise, it will start another new thing

    PROPAGATION_SUPPORTS

    Indicates that the current method does not need a transaction context, but if there is a current transaction, the method will run in the transaction

    PROPAGATION_MANDATORY

    Indicates that the method must run in a transaction. If the current transaction does not exist, an exception will be thrown

    PROPAGATION_NEQUIRED_NEW

    Indicates that the current method must run in its own transaction. A new transaction will be started. If there is a current transaction, this method is used. During execution, the current transaction will be suspended. If JTAransactionManager is used, you need to access the transaction manager

    PROPAGATION_NOT_SUPPORTED

    Indicates that the method should not run in a transaction. If there is a current transaction, the current transaction will be suspended while the method is running. If you use JATransactionManager, you need to access the TransactionManager

    PROPAGATION_NEVER

    Indicates that the current method should not run in a transaction context. If a transaction is currently running, an exception is thrown

    PROPAGATION_NESTED

    Indicates that if a transaction currently exists, the method will run in the absconded transaction. Nested transactions can be committed or rolled back independently of the current transaction. If the current transaction does not exist, its behavior is the same as PROPAGATON_REQUIRED. Note that different vendors support this propagation behavior. Refer to the documentation of the resource manager to confirm whether they support nested transactions

II Spring transaction operations

Case: simple simulated transfer

dao

public interface ICounterDao {
    void updateIncreaseManyById(Integer id,Integer many);
    void updateDecreaseManyById(Integer id,Integer many);
}
@Repository("getCounterDao")
public class CounterDao implements ICounterDao {
    // Create a JdbcTemplate
    @Resource(name = "getJdbcTemplate")
    private JdbcTemplate jdbcTemplate;

    @Override
    public void updateIncreaseManyById(Integer id, Integer many) {
        String sql = "update counter set cmany= cmany + ? where id = ?";
        int update = jdbcTemplate.update(sql, many, id);
        System.out.println("Update results "+update);
    }
    @Override
    public void updateDecreaseManyById(Integer id, Integer many) {
        String sql = "update counter set cmany= cmany - ? where id = ?";
        int update = jdbcTemplate.update(sql, many, id);
        System.out.println("Update results "+update);
    }
}

service

public interface ICounterService {
    void transfer();
}
@Service("CounterService")
public class CounterService implements ICounterService {
    // Persistent operation
    @Resource(name = "getCounterDao")
    private ICounterDao CounterDao;

    @Override
    public void transfer() {

        // Simple demonstration, write code directly here
        // For example, Zhang San (1) transfers 200 money to Li Si (2)
        CounterDao.updateDecreaseManyById(1, 1000);

        // Throw exception manually

        System.out.println(10 / 0);
        CounterDao.updateIncreaseManyById(2, 1000);


    }
}

xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"

       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop  http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--Scan component-->
    <context:component-scan base-package="com.zhj"/>
<!--Import jdbc-->
    <context:property-placeholder location="jdbc.properties"/>
<!--create data source-->
    <bean id="getDataSourcePool" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
        <property name="driverClass" value="${jdbc.driver}" />
        <property name="jdbcUrl" value="${jdbc.url}" />
        <property name="user" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>
    <bean id="getJdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg name="dataSource" ref="getDataSourcePool" />
    </bean>

</beans>

test

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class MyTest {
    @Resource(name = "CounterService")
    private CounterService counterService;
    @Test
    public void test(){
        counterService.transfer();
    }

}

When an exception occurs, the data is changed only once, which violates the principles of atomicity and consistency of the database

III Implementation of transactions in Spring

Declarative transaction management

Declarative transaction management based on @ Transactional annotation

  • Modify the configuration xml to configure the transaction manager

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xmlns:aop="http://www.springframework.org/schema/aop"
    
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop  http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/tx  http://www.springframework.org/schema/tx/spring-tx.xsd
    ">
    <!--Scan component-->
        <context:component-scan base-package="com.zhj"/>
    <!--Import jdbc-->
        <context:property-placeholder location="jdbc.properties"/>
    <!--create data source-->
        <bean id="getDataSourcePool" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
            <property name="driverClass" value="${jdbc.driver}" />
            <property name="jdbcUrl" value="${jdbc.url}" />
            <property name="user" value="${jdbc.username}" />
            <property name="password" value="${jdbc.password}" />
        </bean>
        <bean id="getJdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <constructor-arg name="dataSource" ref="getDataSourcePool" />
        </bean>
    
    
        <!--Transaction manager-->
        <bean id="txManager"
              class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
            <property name="dataSource" ref="getDataSourcePool"/>
        </bean>
        <!--Support transaction annotations-->
        <tx:annotation-driven transaction-manager="txManager" />
    
    
    </beans>
    
  • Add @ Transactional to the required method

    @Service("CounterService")
    public class CounterService implements ICounterService {
        // Persistent operation
        @Resource(name = "getCounterDao")
        private ICounterDao CounterDao;
    
    
        @Override
        // What exception does the isolation level propagation path encounter to roll back
        @Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED,rollbackFor = ArithmeticException.class)
        public void transfer() {
    
            // Simple demonstration, write code directly here
            // For example, Zhang San (1) transfers 200 money to Li Si (2)
            CounterDao.updateDecreaseManyById(1, 1000);
    
            // Throw exception manually
    
            if(true){
                System.out.println(10/0);
            }
    
            CounterDao.updateIncreaseManyById(2, 1000);
    
    
        }
    }
    
  • The call uses the corresponding interface to receive

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    public class MyTest {
        @Resource(name = "CounterService")
        private ICounterService counterService;
        @Test
        public void test(){
            counterService.transfer();
        }
    
    }
    

Keywords: Java

Added by bennyboywonder on Mon, 10 Jan 2022 17:16:14 +0200