Spring novice (transaction)

Transactions in Spring

1. Business Review

Transaction: a logical set of operations, either all successful or all failed. Transaction characteristics: ACID

Atomicity: transaction indivisibility consistency: data integrity is consistent before and after transaction execution. Isolation: when a transaction is executed, it should not be disturbed by other transactions. Persistence: once it is completed, the data will be permanently saved to the database

If isolation is not considered: dirty reading: when a transaction reads uncommitted data from another transaction, it cannot be read repeatedly: when a transaction reads that another transaction has committed data (update), the results of multiple queries of a transaction are inconsistent. Virtual reading: when a transaction reads that another transaction has committed data (insert), the results of multiple queries of a transaction are inconsistent

Transaction isolation level: uncommitted read: all of the above can occur. Committed reads: avoid dirty reads, but do not repeat them. Virtual reads are possible. Repeatable reading: avoid dirty reading and non repeatable reading, but virtual reading may occur. Serial: avoid all of the above

2. Transactions in spring

Hierarchical development of transaction management in Spring: transactions are in the Service layer

Spring provides transaction management API s

Platform transaction manager: platform transaction manager

getTransaction(TransactionDefinition definition) ​

rollback(TransactionStatus status) ​

commit(TransactionStatus status) ​

TransactionDefinition: transaction definition

ISOLation_ 30: Transaction isolation level

PROPAGATION_ 30: The propagation of transactions

TransactionStatus: transaction status , whether there is a save point , whether it is a new transaction , whether the transaction has been committed , relationship: PlatformTransactionManager sets transaction related information through TransactionDefinition to manage transactions. During the process of managing transactions, some transaction statuses are generated, and the statuses are recorded by TransactionStatus.

API details: PlatformTransactionManager: interface. Spring provides different PlatformTransactionManager interface implementations for different persistence frameworks

When using Spring JDBC or iBatis to persist data, use (key) org.springframework.jdbc.datasource.DataSourceTransactionManager

When using Hibernate to persist data, use} org.springframework.orm.Hibernate.hibernatetransaction manager

Used when using JPA for persistence

org.springframework.orm.jpa.JpaTransactionManager

Used when the persistence mechanism is Jdo

org.springframework.jdo.JdoTransactionManager

Use a JTA implementation to manage transactions. When a transaction spans multiple resources, you must use {org.springframework.transaction.jta.jtatatransactionmanager

TransactionDefinition: ​ ISOLATION_DEFAULT: default level

Mysql --> repeatable_read | Oracle -->> read_commited

The levels are as follows:

ISOLATION_READ_UNCOMMITTED ​

ISOLATION_READ_COMMITTED ​

ISOLATION_REPEATABLE_READ ​

ISOLATION_SERIALIZABLE

3. Propagation behavior of transactions in spring

Transaction propagation behavior: (not JDBC transaction management, used to solve actual development problems.) propagation behavior: solve the relationship between invoked transactions between business layers

PROPAGATION_REQUIRED: supports the current transaction. If it does not exist, create a new one

  • A. B if a has a transaction, B uses the transaction of A. if a has no transaction, B starts a new transaction. (A and B are in a transaction.)

PROPAGATION_SUPPORTS: supports the current transaction. If it does not exist, it will not be used

  • A. B if a has a transaction, B uses a's transaction. If a has no transaction, B does not use a transaction

PROPAGATION_MANDATORY: supports the current transaction. If it does not exist, an exception will be thrown

  • A. B if a has a transaction, B uses a's transaction. If a has no transaction, an exception is thrown

PROPAGATION_REQUIRES_NEW: if a transaction exists, suspend the current transaction and create a new transaction

  • A. B if a has a transaction, B suspends a's transaction and creates a new transaction. (A and B are not in a transaction. Transactions do not affect each other.)

PROPAGATION_NOT_SUPPORTED: runs in a non transactional manner. If a transaction exists, suspend the current transaction

  • A. B runs in a non - transactional manner. If a has a transaction, it will suspend the current transaction

PROPAGATION_NEVER: run in non transaction mode. If there is a transaction, throw an exception PROPAGATION_NESTED: if the current transaction exists, the nested transaction is executed

  • Based on SavePoint technology

    A. B a has a transaction. After a executes, save the content after a transaction execution to SavePoint.B. if the transaction is abnormal, the user needs to set whether the transaction is committed or rolled back

  • Common: (key) promotion_ REQUIRED PROPAGATION_ REQUIRES_ NEW PROPAGATION_ NESTED

4. Transaction management in spring

Spring's transaction management is divided into two types: programmatic transaction management: manually writing code to complete transaction management. (understand) declarative transaction management: no need to manually write code and configure

5.Spring transaction case

Simulate the problem of transfer between two people. When we do not join the transaction operation, if the transfer out operation is executed, an exception is thrown between the transfer out operation and the transfer in operation. As we all know, the program will stop when it encounters an exception, so the result is that this person transfers the money and the other person does not receive the money. There is a big bug, so we introduce the transaction operation

5.1 complete transaction management by manual coding (understand)

Disadvantages: the amount of code increases and the code is invasive

    (1) Create database and money table

(2) Create db.properties file

db.driverClassName = com.mysql.cj.jdbc.Driver
db.url = jdbc:mysql://localhost:3306/javacoffee?serverTimezone=Asia/Shanghai&characterEncoding=UTF8
db.username=root
db.password=1234

(3) Import dependencies, I will directly copy them here, including the packages required below

<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.10</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.10</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.3.10</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.6</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.26</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.6</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
        </dependency>
    </dependencies>

(4) Create mapper and implementation classes and define methods to transfer out money and increase money

package com.coffee.mapper;

public interface Money {

    public void setMoney(String money,Integer id);

    public void getMoney(String money,Integer id);

}
package com.coffee.mapper.impl;

import com.coffee.mapper.Money;
import org.springframework.jdbc.core.support.JdbcDaoSupport;

public class MoneyImpl extends JdbcDaoSupport implements Money {


    @Override
    public void setMoney(String money,Integer id) {
        String sql = "update money set money = money - ? where id = ?";
        getJdbcTemplate().update(sql,money,id);

    }

    @Override
    public void getMoney(String money,Integer id) {
        String sql = "update money set money = money + ? where id = ?";
        getJdbcTemplate().update(sql,money,id);
    }
}

(5) Create a service and its implementation class

package com.coffee.service;

import java.sql.SQLException;

public interface MoneyService {
    public void money(String money,Integer id1,Integer id2) throws SQLException;
}

In the implementation class, the transaction template object is used

package com.coffee.service.impl;

import com.coffee.mapper.impl.MoneyImpl;
import com.coffee.service.MoneyService;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;


public class MoneyServiceImpl implements MoneyService {

    private MoneyImpl moneyImpl;
    private TransactionTemplate transactionTemplate;

    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
        this.transactionTemplate = transactionTemplate;
    }

    public void setMoneyImpl(MoneyImpl moneyImpl) {
        this.moneyImpl = moneyImpl;
    }

    @Override
    public void money(String money, Integer id1, Integer id2) {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                moneyImpl.setMoney(money,id1);
//                You can write an exception here to see if the transaction rollback is executed
//                int sb= 1/0;
                moneyImpl.getMoney(money,id2);
            }
        });
    }
}

(6) Complete dependency injection in applicationContext.xml file

<?xml version="1.0" encoding="UTF-8"?>
        <beans 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/tx
         http://www.springframework.org/schema/tx/spring-tx.xsd
         http://www.springframework.org/schema/aop
         http://www.springframework.org/schema/aop/spring-aop.xsd"
               xmlns:tx="http://www.springframework.org/schema/tx"
               xmlns:context="http://www.springframework.org/schema/context"
               xmlns:aop="http://www.springframework.org/schema/aop"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns="http://www.springframework.org/schema/beans">

<!--        Scan the file to obtain the database properties stored in the file-->
        <context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<!--        Inject four attributes to obtain the channel database connection object datasource-->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
                <property name="username" value="${db.username}"></property>
                <property name="password" value="${db.password}"></property>
                <property name="url" value="${db.url}"></property>
                <property name="driverClassName" value="${db.driverClassName}"></property>
        </bean>
<!--        The transaction core manager, which encapsulates all transaction operations, is very important-->
        <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
                <property name="dataSource" ref="dataSource"></property>
        </bean>
<!--        Transaction template object-->
        <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
                <property name="transactionManager" ref="dataSourceTransactionManager"></property>
        </bean>
<!--        mapper Layer, using JdbcTemplate To modify, so it needs to be passed in datasource-->
        <bean id="moneyImpl" class="com.coffee.mapper.impl.MoneyImpl">
                <property name="dataSource" ref="dataSource"></property>
        </bean>
<!--        service Layer, will mapper Dependency injection is performed on the objects obtained in the layer, as well as the transaction template object-->
        <bean id="moneyService" class="com.coffee.service.impl.MoneyServiceImpl">
                <property name="moneyImpl" ref="moneyImpl"></property>
                <property name="transactionTemplate" ref="transactionTemplate"></property>
        </bean>
</beans>

(7) Test class

import com.coffee.service.MoneyService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.sql.SQLException;

public class MyTest {
    @Test
    public void test(){
        ClassPathXmlApplicationContext classPathXmlApplicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        MoneyService money2 = (MoneyService)classPathXmlApplicationContext.getBean("moneyService");
        try {
//The three parameters are the money transferred, the id of the account transferred out, and the id of the account transferred in
            money2.money("100",1,2);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

Both of them had 1000 yuan. If the account id is 1, 100 yuan will be transferred to the account id is 2

give the result as follows

  When an exception occurs immediately after the transfer out, the transaction rollback will be executed. If an exception occurs between the two methods

  After executing the console output statement:

  The database is:

  You can see that the balance of the two accounts has not changed

5.7 XML configuration (aop) to complete transaction management

Modify AccountServiceImpl

package com.coffee.service.impl;

import com.coffee.mapper.impl.MoneyImpl;
import com.coffee.service.MoneyService;

public class MoneyServiceImpl implements MoneyService {

    private MoneyImpl moneyImpl;


    public void setMoneyImpl(MoneyImpl moneyImpl) {
        this.moneyImpl = moneyImpl;
    }

    @Override
    public void money(String money, Integer id1, Integer id2) {
          {
                moneyImpl.setMoney(money,id1);
//                You can write an exception here to see if the transaction rollback is executed
                int sb= 1/0;
                moneyImpl.getMoney(money,id2);
            }
    }
}

Modify applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
        <beans 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/tx
         http://www.springframework.org/schema/tx/spring-tx.xsd
         http://www.springframework.org/schema/aop
         http://www.springframework.org/schema/aop/spring-aop.xsd"
               xmlns:tx="http://www.springframework.org/schema/tx"
               xmlns:context="http://www.springframework.org/schema/context"
               xmlns:aop="http://www.springframework.org/schema/aop"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns="http://www.springframework.org/schema/beans">

<!--        Scan the file to obtain the database properties stored in the file-->
        <context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<!--        Inject four attributes to obtain the channel database connection object datasource-->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
                <property name="username" value="${db.username}"></property>
                <property name="password" value="${db.password}"></property>
                <property name="url" value="${db.url}"></property>
                <property name="driverClassName" value="${db.driverClassName}"></property>
        </bean>
<!--        The transaction core manager, which encapsulates all transaction operations, is very important-->
        <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
                <property name="dataSource" ref="dataSource"></property>
        </bean>
<!--Configure transaction notifications-->
<!--         &lt;!&ndash; Configuration in Enterprise CRUD Methods generally use method names+wildcard*The method name in the class should be consistent with the configured method name &ndash;&gt;-->
<!--               &lt;!&ndash; In method,Specifies what transaction properties the method applies-->
<!--                isolation: Specifies the isolation level of the transaction. The default value is DEFAULT,Indicates that the default isolation level of the database is used.-->
<!--                propagation: Specifies the propagation behavior of the transaction. The default value is REQUIRED,It means that there will be transactions, adding, deleting and modifying options. The query method can be selected SUPPORTS. -->
<!--                read-only: Specifies whether the transaction is read-only. Only query methods can be set to true. The default value is false,Indicates reading and writing.-->
<!--                timeout: Used to specify the timeout of a transaction. The default value is-1,Means never timeout. If a value is specified, in seconds.-->
<!--                rollback-for: Used to specify an exception. When the exception is generated, the transaction will be rolled back. When other exceptions are generated, the transaction will not be rolled back. There is no default value. Indicates that any exceptions are rolled back.-->
<!--                no-rollback-for: Used to specify an exception. When the exception is generated, the transaction will not be rolled back. When other exceptions are generated, the transaction will be rolled back. There is no default value. Indicates that any exceptions are rolled back.-->
<!--             &ndash;&gt;-->
        <tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager">
                <tx:attributes>
                       <tx:method name="money" propagation="REQUIRED" isolation="REPEATABLE_READ" read-only="false"></tx:method>
                </tx:attributes>
        </tx:advice>


<!--        Transaction template object-->
        <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
                <property name="transactionManager" ref="dataSourceTransactionManager"></property>
        </bean>
<!--        mapper Layer, using JdbcTemplate To modify, so it needs to be passed in datasource-->
<!--       Configuration weaving-->
        <aop:config>
<!--                Configure pointcuts-->
                <aop:pointcut id="pc" expression="execution(* com.coffee.service.impl.MoneyServiceImpl.money(..))"/>
<!--                Configure section-->
                <!-- Configure section : notice+Tangent point advice-ref:Name of the notification pointcut-ref:Name of tangent point -->
                <aop:advisor advice-ref="txAdvice" pointcut-ref="pc"></aop:advisor>
        </aop:config>

        <bean id="moneyImpl" class="com.coffee.mapper.impl.MoneyImpl">
                <property name="dataSource" ref="dataSource"></property>
        </bean>
<!--        service Layer, will mapper Dependency injection is performed on the objects obtained in the layer, as well as the transaction template object-->
        <bean id="moneyService" class="com.coffee.service.impl.MoneyServiceImpl">
                <property name="moneyImpl" ref="moneyImpl"></property>
        </bean>
</beans>

5.8 complete transaction management by annotation configuration (aop)

Modify applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
        <beans 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/tx
         http://www.springframework.org/schema/tx/spring-tx.xsd
         http://www.springframework.org/schema/aop
         http://www.springframework.org/schema/aop/spring-aop.xsd"
               xmlns:tx="http://www.springframework.org/schema/tx"
               xmlns:context="http://www.springframework.org/schema/context"
               xmlns:aop="http://www.springframework.org/schema/aop"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns="http://www.springframework.org/schema/beans">

<!--        Scan the file to obtain the database properties stored in the file-->
        <context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<!--        Inject four attributes to obtain the channel database connection object datasource-->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
                <property name="username" value="${db.username}"></property>
                <property name="password" value="${db.password}"></property>
                <property name="url" value="${db.url}"></property>
                <property name="driverClassName" value="${db.driverClassName}"></property>
        </bean>
<!--        The transaction core manager, which encapsulates all transaction operations, is very important-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
                <property name="dataSource" ref="dataSource"></property>
        </bean>
<!--        Open annotation transaction-->
        <tx:annotation-driven/>

        <bean id="moneyImpl" class="com.coffee.mapper.impl.MoneyImpl">
                <property name="dataSource" ref="dataSource"></property>
        </bean>
<!--        service Layer, will mapper Dependency injection is performed on the objects obtained in the layer, as well as the transaction template object-->
        <bean id="moneyService" class="com.coffee.service.impl.MoneyServiceImpl">
                <property name="moneyImpl" ref="moneyImpl"></property>
        </bean>
</beans>

Modify AccountServiceImpl

package com.coffee.service.impl;

import com.coffee.mapper.impl.MoneyImpl;
import com.coffee.service.MoneyService;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

public class MoneyServiceImpl implements MoneyService {

    private MoneyImpl moneyImpl;


    public void setMoneyImpl(MoneyImpl moneyImpl) {
        this.moneyImpl = moneyImpl;
    }

    @Override
    //Annotations can be directly represented on the class, but if the method is different from the configuration on the class name, annotations can also be configured on this method alone
    @Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRED,readOnly = false)
    public void money(String money, Integer id1, Integer id2) {
          {
                moneyImpl.setMoney(money,id1);
//                You can write an exception here to see if the transaction rollback is executed
                int sb= 1/0;
                moneyImpl.getMoney(money,id2);
            }
    }
}

Summary: the method of configuring AOP and annotation with xml is relatively simple, and it is often used in development

Keywords: Spring

Added by KissMyBrain on Mon, 18 Oct 2021 23:32:57 +0300