[Spring from entry to actual combat tutorial] Chapter 6 Detailed Explanation of Spring Transaction Management

6. Spring transaction management

6.1 Introduction to Transactions

    Transaction is an important part of enterprise applications based on relational database (RDBMS). In the field of software development, transaction players play a very important role to ensure the integrity and consistency of application data.

    A transaction is a series of actions that, as an independent unit of work, either all succeed or all fail.

    Transactions have four characteristics: atomicity, consistency, isolation, and durability, referred to as ACID characteristics.
        Atomicity: A transaction is an indivisible unit of work, and the actions included in the transaction are either done or not done.
        Consistency: Transactions must ensure that the database changes from one consistent state to another. Consistency and atomicity are closely related.
        Isolation: The execution of a transaction cannot be interfered by other transactions, that is, the operations and data used within a transaction are isolated from other concurrent transactions, and the concurrently executed transactions cannot interfere with each other.
        Durability: Persistence is also called permanence, which means that once a transaction is committed, its changes to the data in the database are permanent, and other subsequent operations and failures should not have any impact on it.
        
    Transactions allow us to combine several operations or groups of operations into a unit of work that either all succeeds or all fails. If all operations in the transaction are executed successfully, then everything will be fine. But if any operation in the transaction fails, all operations in the transaction will be rolled back, and the successful operations will be completely cleared, as if nothing happened.

    Probably the most common transaction-related example in the real world is bank transfers. Suppose we need to transfer 1,000 yuan from account A to account B. This transfer operation involves the following two operations.
        Deduct 1,000 yuan from account A;
        deposit 1,000 yuan into account B.

    If account A successfully deducts 1,000 yuan, but fails to deposit to account B, then we will lose 1,000 yuan out of thin air; if account A fails to deduct money, but successfully deposits 1,000 yuan to account B, we will lose If there is an extra 1,000 yuan in the account, the bank will suffer losses. Therefore, we must ensure that all operations in the transaction are either all successful or all fail. Understanding this, we have grasped the core of the transaction.

    As an excellent open source framework and application platform, Spring also provides good support for transactions. With the help of the powerful configuration capabilities of the IoC container, Spring provides rich functional support for transactions.

6.2 Problems with native JDBC transaction management

Native JDBC transaction management implementation:

//1、获取Connection对象
Connection conn = DBUtil.getConnection();

//2、获取执行SQL语句的Statement
PreparedStatement stmt = null;

try {
    //3、修改事务的提交方式为手动提交
    conn.setAutoCommit(false);
    
    //4、执行SQL
    String sql = "insert into ...";
    stmt = conn.prepareStatement(sql);
    //设置占位符
    stmt.setString(1, ...);
    stmt.executeUpdate();
    
    //5、手动提交
    conn.commit();
} catch (SQLException e) {
    e.printStackTrace();
    try{
        //6、出现异常回滚
        conn.rollback();
    } catch (SQLException e1) {
        e1.printStackTrace();
    }
} finally {
    //7、释放资源
    DBUtil.closeAll(conn, stmt, null);
}

Spring native JDBC transaction management implementation:

public class JdbcTest {

    @Test
    public void jdbcTemplateTxTest() {
        //1、获取IOC容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

        //2、获取数据源
        DataSource dataSource = ac.getBean("dataSource", DataSource.class);


        //3、获取连接
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        try {
            connection = dataSource.getConnection();

            //4、修改jdbc事务的提交方式为手动提交
            connection.setAutoCommit(false);

            //5、获取执行SQL的Statement,并执行
            String sql = "delete from student where id = 3";
            preparedStatement = connection.prepareStatement(sql);
            int result = preparedStatement.executeUpdate();

            System.out.println("执行成功:" + result);

            //6、手动提交
            connection.commit();
        } catch (SQLException throwables) {
            // 7、出现异常回滚
            try {
                connection.rollback();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            throwables.printStackTrace();
        } finally {
            //8、释放资源
            if (preparedStatement != null) {
                try {
                    preparedStatement.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }

            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
    }
}

question:

    Similar boilerplate code would have to be rewritten for a different approach. And this code is specific to JDBC. Once you choose other database access technologies (for example, Mybatis uses SqlSession to submit and rollback), the code needs to be modified accordingly.

6.3 Transaction Management in Spring

  • As an enterprise application framework, Spring defines an abstraction layer on top of different transaction management APIs. Application developers do not need to understand the underlying transaction management API to use Spring's transaction management mechanism.

  • Spring supports both programmatic and declarative transaction management.

  • Programmatic transaction management: embed transaction management code into business methods to control transaction commit and rollback. When managing transactions programmatically, you must include additional transaction management code in each transaction operation.

  • Declarative transaction management: In most cases, it is better to use than programmatic transaction management. It separates transaction management code from business methods and implements transaction management in a declarative manner. Transaction management, as a cross-cutting concern, can be modularized through the AOP approach. Spring supports declarative transaction management through the AOP framework.

Choosing between programmatic and declarative transactions is largely a trade-off between fine-grained control and ease of use.

  • Programmatic control of things is more fine-grained. We can precisely control the boundaries of transactions. The start and end of transactions depend entirely on our needs, but there is a fatal shortcoming in this method, that is, the coupling of transaction rules and business codes The degree is high and difficult to maintain, so we rarely use this method to manage transactions.

  • Declarative transactions are easier to use, non-invasive to business code, low in coupling, and easy to maintain, so this method is also our most commonly used transaction management method.

Spring's declarative transaction management is mainly implemented in the following two ways:

  • XML-based declarative transaction management

  • Annotation-based declarative transaction management

6.4 Transaction Manager

  • Spring abstracts a whole set of transaction mechanisms from different transaction management APIs. Developers do not need to know the underlying transaction API to take advantage of these transaction mechanisms. With these transaction mechanisms, transaction management code can be independent of specific transaction technology.

  • Spring does not manage transactions directly, but manages transactions through a transaction manager. Spring's core transaction manager is the PlatformTransactionManager interface. It encapsulates a set of technology-independent methods for transaction management. Regardless of which transaction management strategy (programmatic or declarative) Spring uses, a transaction manager is a must.

  • Each method in the PlatformTransactionManager interface is described as follows:

name illustrate
TransactionStatus getTransaction(TransactionDefinition definition) Used to get status information about transactions
void commit(TransactionStatus status) for submitting transactions
void rollback(TransactionStatus status) for rolling back transactions
  • Spring provides different PlatformTransactionManager interface implementations for different persistence frameworks or platforms (such as JDBC, Hibernate, JPA, and JTA, etc.), and these implementation classes are called transaction manager implementations.

Implementation class illustrate
org.springframework.jdbc.datasource.DataSourceTransactionManager Used when using Spring JDBC or iBatis to persist data.
org.springframework.orm.hibernate3.HibernateTransactionManager Used when using Hibernate 3.0 and above to persist data.
org.springframework.orm.jpa.JpaTransactionManager Used when using JPA for persistence.
org.springframework.jdo.JdoTransactionManager Used when the persistence mechanism is Jdo.
org.springframework.transaction.jta.JtaTransactionManager Use JTA to implement transaction management, which is used when a transaction spans multiple different resources (ie, a distributed transaction).
  • The use of these transaction managers is very simple. We only need to select the corresponding transaction manager implementation according to the persistence framework (or platform) to realize the management of things without caring about what the actual transaction implementation is.

6.5 Transaction Case

6.5.1 Namespace and label specification

Add the tx namespace and tag specification to the spring configuration file:

xmlns:tx="http://www.springframework.org/schema/tx"

http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd

6.5.2 Spring configuration

<?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:util="http://www.springframework.org/schema/util"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/util
    http://www.springframework.org/schema/util/spring-util.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">

    <!-- 组件扫描 -->
    <context:component-scan base-package="com.newcapec"/>

    <!-- 读取资源文件 -->
    <context:property-placeholder location="classpath:db.properties"/>

    <!-- 配置数据源 -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <!-- 连接数据库的基础信息 -->
        <property name="driverClassName" value="${jdbc.mysql.driver}"/>
        <property name="url" value="${jdbc.mysql.url}"/>
        <property name="username" value="${jdbc.mysql.username}"/>
        <property name="password" value="${jdbc.mysql.password}"/>
    </bean>

    <!-- 配置JdbcTemplate -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
</beans>

6.5.3 Create table structure

CREATE TABLE T_ACCOUNT(
  id      INT PRIMARY KEY AUTO_INCREMENT,
  name    VARCHAR(20),
  balance DECIMAL(8,2)
);

insert into T_ACCOUNT (id, name, balance) values (1, '小明', 1000);
insert into T_ACCOUNT (id, name, balance) values (2, '淘宝', 0);
insert into T_ACCOUNT (id, name, balance) values (3, '京东', 0);

6.5.4 Entity classes

public class Account {
    private Integer id;
    private String name;
    private Double balance;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public Double getBalance() {
        return balance;
    }

    public void setBalance(Double balance) {
        this.balance = balance;
    }
}

6.5.5 Dao

interface:

public interface AccountDao {

    void updateBalance(Account account);

    Account findAccountById(Integer id);
}

Implementation class:

/**
 * 注意:事务不能添加在持久层,而是放在业务层
 * 持久层中的方法都是单sql,业务层可能由多sql组成
 */
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void updateBalance(Account account) {
        String sql = "update t_account set balance=? where id=?";
        jdbcTemplate.update(sql, account.getBalance(), account.getId());
        System.out.println("对账户的余额更新操作完成..........");
    }

    @Override
    public Account findAccountById(Integer id) {
        String sql = "select id,name,balance from t_account where id=?";
        RowMapper<Account> rowMapper = new BeanPropertyRowMapper<>(Account.class);
        return jdbcTemplate.queryForObject(sql, rowMapper, id);
    }
}

6.5.6 Service

interface:

public interface AccountService {

    void transferMoney(int fromId, int toId, double money) throws Exception;
}

Implementation class:

@Service("accountService")
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    @Override
    public void transferMoney(int fromId, int toId, double money) throws Exception {
        /**
         * 在转账业务逻辑中包含以下的几个步骤
         * 1.查询转出账户的余额
         * 2.从转出账户中扣除转账金额
         * 3.查询转入账户的余额
         * 4.转入账户的余额+转账金额,并更新转入账户的余额
         */
        Account fromAccount = accountDao.findAccountById(fromId);
        fromAccount.setBalance(fromAccount.getBalance() - money);
        accountDao.updateBalance(fromAccount);

        //模拟程序执行过程中出现问题,异常出现导致程序终止(方法结束)
        //System.out.println(10/0);

        Account toAccount = accountDao.findAccountById(toId);
        toAccount.setBalance(toAccount.getBalance() + money);
        accountDao.updateBalance(toAccount);

        System.out.println("一次转账完成...");
    }
}

6.5.7 No transaction test

public class TXTest {

    @Test
    public void testTransfer() throws Exception {
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        AccountService accountService = ac.getBean("accountService", AccountService.class);
        accountService.transferMoney(1,2,360);
    }
}

Analysis of test results:

  • When there is no exception in the Service method, the program is executed normally, and the data in the database is also normal. The balance in Xiaoming's account is 640, and the balance in Taobao's account is 360;

  • When an exception occurs in the method of Service, the execution of the program is terminated due to an exception, and there is a problem with the data in the database (data inconsistency). The balance in Xiaoming's account is 640, the balance in Taobao's account is 0, and the transferred amount data is lost;

6.5.8 Configure transaction management

    Spring's declarative transaction management is implemented through AOP. Its essence is to intercept the method before and after, and then create (or join) a transaction before the target method starts. After the target method is executed, the transaction is committed or rolled back according to the execution status.

    The biggest advantage of declarative transactions is that they are less intrusive to business code, and can well decouple business code and transaction management code.

Configure Transaction Manager

<!-- 配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- 注入数据源对象 -->
    <property name="dataSource" ref="dataSource"/>
</bean>

In the above configuration, the configured transaction manager is implemented as DataSourceTransactionManager, which is the implementation of the PlatformTransactionManager interface provided by JDBC and iBatis.

Configure Transaction Notifications

    Configure transaction advice in Spring's XML configuration file, specifying the method of transaction action and the required transaction attributes.
    
    When we use <tx:advice> to declare a transaction, we need to define a transaction manager through the transaction-manager parameter, and the value of this parameter is transactionManager by default.

    If the transaction manager we set ourselves happens to be the same as the default value, we can omit the configuration of the parameters. But if the transaction manager id we set ourselves is different from the default value, we must manually specify it through the transaction-manager parameter in the <tx:advice> element.
    
    For <tx:advice>, transaction attributes are defined in <tx:attributes>, which can contain one or more <tx:method> elements.

    The <tx:method> element contains multiple attribute parameters, which can define transaction attributes for one or some specified methods (methods defined by the name attribute), as shown in the following table.

transaction attribute illustrate
propagation Specifies the propagation behavior for transactions.
isolation Specifies the isolation level for transactions.
read-only Specifies whether it is a read-only transaction.
timeout Indicates the timeout period, the unit is "seconds"; the declared transaction will be automatically rolled back after the specified timeout period, so as to avoid the occupation of database resources caused by the rollback if the transaction is not committed for a long time.
rollback-for Specifies that the transaction should be rolled back, not committed, for those types of exceptions.
no-rollback-for Specifies that the transaction should continue running, not rolled back, for those exceptions.
<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <!--配置事务属性-->
    <tx:attributes>
        <!--配置那些方法要使用事务,事务的属性-->
        <!-- 所有的事务管理的方法中,采用默认的事务属性 -->
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>

Configure transaction entry point

    The <tx:advice> element just defines an AOP advice, it is not a complete transactional aspect. We didn't define which beans should be advised in the <tx:advice> element, so we need a pointcut to do this.

    In Spring's XML configuration, we can use Spring AOP technology to configure transaction notification (tx-advice) and cut points into aspects. The configuration content is as follows.

<!-- 配置切点和切面 -->
<aop:config><!--配置切点-->
    <aop:pointcut id="exp" expression="execution(* com.newcapec.service..*Impl.*(..))"/>
    <!-- 配置切面: 将切点表达式应用在事务上-->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="exp"/>
</aop:config>

transaction test

    When an exception occurs in the Service method, the execution of the program is terminated due to an exception, and the data in the database is still the original data. The balance in Xiaoming's account is 1000, and the balance in Taobao's account is 0, and the transfer-out and transfer-in of the amount has not been implemented (partial data rollback after successful execution).

6.6 Attributes of Transactions

6.6.1 Transaction propagation behavior

  • Transaction propagation behavior (propagation behavior) refers to how a transaction method should behave when it is called by another transaction method. For example, when transaction method A calls transaction method B, whether method B continues to run in the transaction of caller A's method, or starts a new transaction for itself, is determined by the transaction propagation behavior of transaction method B. The transaction method refers to the method that can change the data in the database table, such as the method of adding data, deleting data, and modifying data.

  • The propagation behavior of the transaction can be specified by the propagation attribute, and <tx:method>the propagation attribute is configured in the label.

  • Spring defines 7 kinds of propagation behaviors: REQUIRED is the default value.

propagation properties describe
REQUIRED The default propagation behavior, if there is a transaction running, the current method will run in this transaction, otherwise, start a new transaction and run it in its own transaction.
REQUIRES_NEW The current method must start a new transaction and run within its own transaction. If a transaction is running, it should be suspended.
SUPPORTS If a transaction is running, the current method runs within that transaction. Otherwise it may not run in a transaction.
NOT_SUPPORTED The current method should not run within a transaction. If there is a transaction running, suspend it.
MANDATORY The current method must run inside a transaction, and throws an exception if there is no running transaction.
NEVER The current method should not run within a transaction. If there is a transaction running, an exception is thrown.
NESTED If there is a transaction running, the current method should run within the transaction's nested transaction. Otherwise, a new transaction is started and run within its own transaction.
  • REQUIRED propagation behavior

     When the transaction method2() method and method3() method are called by another transaction method method1(), it will run in the existing transaction by default, and the default propagation behavior is REQUIRED. Therefore, there is only one transaction within the start and end boundaries of the method1() method. This transaction is only submitted when the method1() method ends. If an exception occurs during execution, the data of the three methods must be rolled back.

  • REQUIRES_NEW propagation behavior

    The propagation behavior REQUIRES_NEW is that when the transaction method2() method and method3() method are called by another transaction method method1(), both method2() and method3() must start a new transaction, and in method2() and method3() When the transaction is executed, the transaction of the method1() method will be suspended first, and wait for other transactions to complete before continuing to execute. If an exception occurs during execution, if the transactions of method2() or method3() have been executed, they will not roll back the data, only the data of method1() will be rolled back.

    Note: Spring's transaction management is implemented through aspects. If you use this.method() or method() directly, it will not trigger the transaction management in the aspect. You need to define the method in a different class, or inject the service itself, and use this injection object to call the method.

  • Spread Behavior Test

    Add the payOrder method in the Service layer to realize the batch transfer function.

interface:

public interface AccountService {

    void payOrder(int fromId, Map<Integer, Double> map) throws Exception;
}

Implementation class:

@Service("accountService")
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    /**
     * 注意:
     * 同一个Service中不同方法在进行相互调用,不能另起事务,必须在同一个事务内运行
     * 如果需要单独启动事务,需要当前Service类定义一个自己类型属性,并将自己的对象注入
     * 通过该属性调用方法,即可
     */
     @Autowired
    private AccountService accountService;

    @Override
    public void payOrder(int fromId, Map<Integer, Double> map) throws Exception {

        Set<Integer> toIdSet = map.keySet();
        for (Integer toId : toIdSet) {
            double money = map.get(toId);

            //实施单账户转账
            accountService.transferMoney(fromId, toId, money);

            //模拟第一次转账之后,系统出现异常
            String s = null;
            s.trim();
        }
        System.out.println("订单支付成功...");
    }
}

Transaction attribute configuration:

<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <!--配置事务属性-->
    <tx:attributes>
        <tx:method name="transferMoney" propagation="REQUIRES_NEW"/>
        <!--配置那些方法要使用事务,事务的属性-->
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>

test:

@Test
public void testPayOrder() throws Exception {
    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
    AccountService accountService = ac.getBean("accountService", AccountService.class);
    Map<Integer, Double> map = new HashMap<>();
    map.put(2, 199.0);
    map.put(3, 260.0);

    accountService.payOrder(1, map);
}

Analysis of test results:

  • When an exception occurs in the payOrder method and the propagation behavior is REQUIRED, the data in the database is still the original data and has not changed. The data of the first successful transfer is also rolled back along with the exception;

  • When an exception occurs in the payOrder method and the propagation behavior is REQUIRES_NEW, the data in the database changes. The data submission for the first successful transfer has not been rolled back;

6.6.2 Transaction isolation levels

  • When multiple transactions in the same application or different applications execute concurrently on the same data set, the possible problems can be divided into the following three types:

    • Dirty read: For two transactions TX1 and TX2, TX1 reads data that has been updated by TX2 but has not been committed. After that, if TX2 rolls back, the content read by TX1 is temporary and invalid;

    • Non-repeatable read: For two transactions TX1 and TX2, TX1 reads a field, and then TX2 updates the field. After that, TX1 reads the same field again, and the value is different;

    • Phantom read: For two transactions TX1 and TX2, TX1 reads a few rows of data from a table, and then TX2 inserts some new records in the table. Later, if TX1 reads the same table again, there will be a few more rows;

  • In theory, transactions should be completely isolated from each other to avoid problems caused by concurrent transactions. However, that would have a huge impact on performance, since transactions must run sequentially. So in actual development, in order to improve performance, transactions will run at a lower isolation level.

  • The isolation level of the transaction can be specified through the isolation transaction attribute, and <tx:method>the isolation attribute is configured in the label, and its default value is: DEFAULT.

isolation level describe
DEFAULT Use the default isolation level of the underlying database. For most databases, the default isolation level is READ_COMMITTED;
READ_UNCOMMITTED Allows a transaction to read changes not committed by other transactions. Dirty reads, non-repeatable reads, and phantom reads all occur;
READ_COMMITTED Transactions are only allowed to read changes that have been committed by other transactions. Dirty reads can be avoided, but non-repeatable reads and phantom reads may still occur;
REPEATABLE_READ Ensures that a transaction can read the same value from a field multiple times. During the duration of this transaction, other things are prohibited from updating this field. Dirty reads and non-repeatable reads can be avoided, but the problem of phantom reads still exists;
SERIALIZABLE Ensures transactions can read the same rows from one table. During the duration of this transaction, other transactions are prohibited from performing insert, update and delete operations on the table. All concurrency problems can be avoided, but the performance is very low;
  • The transaction isolation level is supported by the underlying database engine, not by the application or framework.

  • Two transaction isolation levels supported by Oracle: READ_COMMITTED, SERIALIZABLE, the default is READ_COMMITTED.

  • Mysql supports 4 transaction isolation levels, the default is REPEATABLE_READ.

Statements related to mysql transaction isolation level:

-- 事务自动提交
show variables like 'autocommit';

-- 关闭OFF
set autocommit = 0;

-- 开启ON
set autocommit = 1;

-- 查看系统级的隔离级别和会话级的隔离级别
select @@global.tx_isolation,@@tx_isolation;

-- 设置事务的隔离级别:set 作用域 transaction isolation level 事务隔离级别名称
-- set [session | global] transaction isolation level {read uncommitted | read committed | repeatable read | serializable}

-- 全局
set global transaction isolation level read committed;

-- 当前会话
set session transaction isolation level read uncommitted;

Oracle transaction isolation level related statements:

-- 设置一个事务的隔离级别:
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

-- 设置单个会话的隔离级别:
ALTER SESSION SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
ALTER SESSION SET TRANSACTION ISOLATION SERIALIZABLE;

-- 首先创建一个事务
declare
    trans_id Varchar2(100);
begin
    trans_id := dbms_transaction.local_transaction_id( TRUE );
end;

-- 查看事务隔离级别
SELECT s.sid, s.serial#,
  CASE BITAND(t.flag, POWER(2, 28))
    WHEN 0 THEN 'READ COMMITTED'
    ELSE 'SERIALIZABLE'
  END AS isolation_level
FROM v$transaction t
JOIN v$session s ON t.addr = s.taddr AND s.sid = sys_context('USERENV', 'SID');

6.6.3 Rollback transaction attributes

  • By default, only runtime exceptions (RuntimeException) will cause the transaction to be rolled back, but compile-time exceptions will not.

  • The rollback rules of the transaction can be configured through <tx:method>the rollback-for and no-rollback-for attributes in the tag. If there are more than one exceptions, separate them with commas.

  • rollback-for: must be rolled back when encountered.

  • no-rollback-for: Must not rollback when encountered.

<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <!-- 事务属性-->
    <tx:attributes>
        <tx:method name="transferMoney" propagation="REQUIRED" isolation="READ_COMMITTED"
                    rollback-for="java.lang.ClassNotFoundException"
                    no-rollback-for="java.lang.NullPointerException"/>
        <!-- 所有的事务管理的方法中,采用默认的事务属性 -->
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>

AccountServiceImpl:

@Override
public void transferMoney(int fromId, int toId, double money) throws Exception {
    /**
         * 在转账业务逻辑中包含以下的几个步骤
         * 1.查询转出账户的余额
         * 2.从转出账户中扣除转账金额
         * 3.查询转入账户的余额
         * 4.转入账户的余额+转账金额,并更新转入账户的余额
         */
    Account fromAccount = accountDao.findAccountById(fromId);
    fromAccount.setBalance(fromAccount.getBalance() - money);
    accountDao.updateBalance(fromAccount);

    //模拟程序执行过程中出现问题,异常出现导致程序终止(方法结束)
    //System.out.println(10/0);

    //编译时异常,默认情况下不会导致事务回滚
    //Class.forName("abc");

    //运行时异常,默认情况下会导致事务回滚
    String str = null;
    str.trim();

    Account toAccount = accountDao.findAccountById(toId);
    toAccount.setBalance(toAccount.getBalance() + money);
    accountDao.updateBalance(toAccount);

    System.out.println("一次转账完成...");
}

test:

@Test
public void testTransfer() throws Exception {
    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
    AccountService accountService = ac.getBean("accountService", AccountService.class);
    accountService.transferMoney(1,2,360);
}

6.6.4 Read-only transaction attributes

    `read-only="true"` in the <tx:method> tag specifies that the transaction is a read-only transaction, which means that the transaction only reads data but does not update data, which can help the database optimize transactions. If this method only reads the data in the database, it should be set as a read-only transaction.

6.6.5 Transaction timeout property

  • How long a transaction can be kept alive before forcing a rollback, which prevents long-running transactions from hogging resources.

  • <tx:method>The timeout attribute in the tag configures the timeout of the transaction. Its value is int type, and the unit is seconds. If the value is -1, it means it will never expire.

  • Two transactions access the same data successively. At this time, the first transaction isolates the data, so the later transaction will not be able to operate the data, and the later transaction will enter the waiting period. The waiting and execution time is the expiration time of the transaction. If the transaction expires then this transaction will be forced to roll back.

6.6.6 Transaction complete configuration

<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <!-- 事务属性-->
    <tx:attributes>
        <!--
            配置事务的方法名称如果以find开头,那么它的事务传播行为为:SUPPORTS
        -->
        <tx:method name="insert*" isolation="READ_COMMITTED"/>
        <tx:method name="add*" isolation="READ_COMMITTED"/>
        <tx:method name="save*" isolation="READ_COMMITTED"/>
        <tx:method name="update*" isolation="READ_COMMITTED"/>
        <tx:method name="edit*" isolation="READ_COMMITTED"/>
        <tx:method name="delete*" isolation="READ_COMMITTED"/>
        <tx:method name="remove*" isolation="READ_COMMITTED"/>
        <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
        <tx:method name="query*" propagation="SUPPORTS" read-only="true"/>
        <tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
        <tx:method name="list*" propagation="SUPPORTS" read-only="true"/>
        <tx:method name="load*" propagation="SUPPORTS" read-only="true"/>
        <tx:method name="select*" propagation="SUPPORTS" read-only="true"/>
        <tx:method name="transferMoney" propagation="REQUIRED" isolation="READ_COMMITTED"
                   rollback-for="java.lang.ClassNotFoundException"
                   no-rollback-for="java.lang.NullPointerException"
                   timeout="4"/>
        <!-- 所有的事务管理的方法中,采用默认的事务属性 -->
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>

Guess you like

Origin blog.csdn.net/ligonglanyuan/article/details/124809858