To protect the security of hundreds of millions of data, Spring has a "declarative transaction" trick

华为云开发者社区
中文
Abstract: takeaway, you only need to consider how to put together orders; when choosing to travel, you only need to figure out the destination; when paying by mobile phone, you only need to ensure that the balance is sufficient. But you don't know that behind these intelligences is the support of hundreds of millions of powerful data. This is the power of databases. Then the huge amount of data will definitely involve data security issues, so how are these accidents and conflicts resolved?

This article is shared from the Huawei Cloud Community " million words explain how Spring uses "declarative transactions" to protect hundreds of millions of data security?丨【Bloom! Database] ", author: Little Grey Ape.

1. What is business management?

To understand declarative transactions, we must start with its basic concepts. So what is a transaction?

In the development of large-scale JavaEE projects, facing the large-scale data, it is necessary to ensure the integrity and consistency of the data, so there is the concept of database transaction, so it is also an indispensable technology for enterprise-level project application development.

transaction can be regarded as a group of multiple database operations that are logically closely related and merged into a whole (unit of work). These operations are either all executed or not executed at all.

At the same time transaction has four very critical attributes (ACID):

  1. atomicity (atomicity) : The original meaning of "atomic" is "inseparable". The atomicity of a transaction shows that multiple operations involved in a transaction are logically indispensable. The atomicity of the transaction requires that all operations in the transaction are either executed or not executed.
  2. Consistency : "Consistency" refers to the consistency of data, which specifically means that all data is in a consistent state that meets business rules. The consistency principle requires: no matter how many operations are involved in a transaction, it must be ensured that the data is correct before the transaction is executed, and the data is still correct after the transaction is executed. If one or several operations fail during the execution of a transaction, all other operations must be undone and the data restored to the state before the transaction was executed, which is a rollback.
  3. Isolation (isolation) : In the actual running process of the application, transactions are often executed concurrently, so it is very likely that there are many transactions processing the same data at the same time, so each transaction should be isolated from other transactions to prevent The data is corrupted. The principle of isolation requires that multiple transactions do not interfere with each other during concurrent execution.
  4. Durability (durability) : The persistence principle requires that after the transaction is executed, the modification of the data is permanently saved, and will not be affected by various system errors or other unexpected situations. Under normal circumstances, the modification of data by the transaction should be written to the persistent memory.

Therefore, transaction control should satisfy these four attributes as much as possible. Since the purpose of transaction control is to be able to perform transaction rollback when an accident occurs in data processing, , and how to deal with these types of errors?

Second, detailed explanation of the use of declarative transactions

Compared with programmatic transactions, declarative transactions have greater advantages. can separate transaction management code from business methods and implement business management in a declarative manner.

As a cross-cutting focus, the fixed pattern of transaction management code can be modularized through the AOP method, and then declarative transaction management can be realized with the help of the Spring AOP framework.

abstraction layer on top of different transaction management APIs, which is enabled by configuration, so that application developers can use Spring's transaction management mechanism without knowing the underlying implementation details of the transaction management API. .

At the same time, Spring supports both programmatic transaction management and declarative transaction management.

So how should declarative transactions be used in Spring?

1. The main implementation of the transaction manager

Spring abstracts a set of transaction management mechanisms from different transaction management APIs, allowing transaction management codes to be independent of specific transaction technologies. this way, we only need to perform transaction management through configuration, without having to understand how the underlying layer is implemented. This is also a big advantage of using declarative transactions.

Spring's core transaction management abstraction is PlatformTransactionManager. It encapsulates a set of technology-independent methods for transaction management. Regardless of Spring's transaction management strategy (programmatic or declarative), the transaction manager is a must.

The transaction manager can be declared in the Spring IOC container in the form of an ordinary bean. The three commonly used transaction managers in Spring are:

  1. DataSourceTransactionManager: Only one data source needs to be processed in the application, and it is accessed through JDBC.
  2. JtaTransactionManager: Use JTA (Java Transaction API) for transaction management on JavaEE application server
  3. HibernateTransactionManager: Use the Hibernate framework to access the database

They are all subclasses of PlatformTransactionManager, and the inheritance diagram is as follows:
image.png

Now that we have a basic understanding of the implementation principles and mechanisms of declarative transactions, it is not as good as practice. Next, we will actually explain how to configure declarative transactions using Spring.

2. Annotation-based declarative transaction configuration

Let me take the DataSourceTransactionManager class as an example to tell you about the implementation process of declarative transactions. friends can implement it. If you have any questions, please leave a message and let me communicate.

(1), configure the data source

Since it is the operation of the database, the first step must be to configure the data source. I believe that the configuration of the data source should be familiar to all of you. Those who don’t know much about it can read my last article about Spring's article. "Spring JDBC Persistence Layer Framework "Family Bucket" Tutorial丨【Bloom! Database] "

To configure the data source, I take the introduction of external data configuration files as an example, so here I need to use the <context></context> tag to import external files, and use the "${}" method to assign values to attributes:

code show as below:

<!-- 连接外部配置文件 -->
<context:property-placeholder location="classpath:jdbcconfig.properties"/>

<!-- 连接数据库 -->
<bean id="pooldataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="user" value="${jdbc.user}"></property>
    <property name="password" value="${jdbc.password}"></property>
    <property name="jdbcUrl" value="${jdbc.jdbcurl}"></property>
    <property name="driverClass" value="${jdbc.driverclass}"></property>
</bean>

(2), the establishment of JdbcTemplate

Since it is operating the database, and it is in the spring framework, it must be necessary for the use of the database operation framework in Spring. I also explained the detailed use of the jdbcTemplate frame technology in the previous article. , Friends can learn!

Here we directly declare the jdbcTemplate class in the bean of the ioc, and set the data source as the data source of the first step.

code show as below:

<!-- 建立jdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="pooldataSource"></property>
</bean>

(3) Conduct transaction control

Now that the data source is configured and the database operation is complete, the next step is today's topic transaction control.

We know that transaction control itself is implemented based on aspect-oriented programming. Therefore, when configuring transaction control, you need to import the corresponding jar package: I have listed the required jar packages for everyone:

• spring-aop-4.0.0.RELEASE.jar
• com.springsource.net.sf.cglib-2.2.0.jar
• com.springsource.org.aopalliance-1.0.0.jar
• com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

Insert a supplement here, which can also be said to be an interview question: Tell me about the advantages of using the transaction manager?

use of transaction control can save the amount of code that is usually written for transaction control. When performing transaction control, if an error occurs during the execution of a transaction, other operations will not be modified, maintaining the atomicity of the transaction.

We use the DataSourceTransactionManager class to configure the transaction manager here.

The specific method is to declare an instance of this class in the bean tag in the ioc, set the id, and assign the data source to the DataSource property.

code show as below:

    <bean id="dataSourceTransactionManager"  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 2、控制住数据源 -->
        <property name="dataSource" ref="pooldataSource"></property>
    </bean>

The transaction manager has been configured in this way, do you think this is the end? No! The next and most critical step! Is to open the transaction manager, because how to use it?

(4) Turn on annotation-based transaction control

annotation-based transaction control is to add corresponding annotations to methods and classes in . Turning on annotation-based transaction control requires the introduction of tx expressions, and using the annotation-driven tag in it, you can enable transaction control for the executing transaction manager.

code show as below:

<!-- 3、开启基于注解的事务控制 -->    
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
<!-- 4、给方法增加事务控制,添加相应的注解-->

The next step is to add corresponding annotations to the method to increase transaction control.

First, the database operation classes generally belong to the business logic layer, so we need to add @service annotations to this class to achieve package scanning, and then add transaction control-specific annotations @Transactional to tell Spring for methods that require transaction control This method is a transaction method. When an error occurs in the operation of this method, other operations on the database in this method will also be rolled back.

code show as below:

@Service
public class BookService {
    @Autowired
    BookDao bookDao;
    /**
     * 顾客买书
     * */
//    开启基于注解的事务控制
    @Transactional
    public void buyBook(String username,String isbn) {
        bookDao.updateStockFromBookStock(isbn);
        int price = bookDao.getPriceFromBook(isbn);
        bookDao.updateBalanceFromAccount(username, price);
        System.out.println("【" +username +  "】买书成功!");
    }
 
}

3. XML-based declarative transaction configuration

Above we explained how to configure declarative transactions using annotations, then there is another way to configure declarative transactions, which is to configure in XML files, and they are all the same when declaring data sources. I won’t talk about it here. Yes, I just talk about how to declare the transaction manager and transaction method through XML after configuring the data source.

(1), configuration transaction aspect

There is a transaction manager (transaction aspect) in Spring, so first we need to configure this transaction aspect.

<aop:config>
    <aop:pointcut expression="execution(* com.spring.service.*.*(..))" id="txpoint"/>
    <!-- 事务建议;advice-ref:指向事务管理器的配置 -->
    <aop:advisor advice-ref="myAdvice" pointcut-ref="txpoint"/>
</aop:config>

(2), configure the transaction manager

Configure the transaction manager to use the tx:advice tag. The attribute transaction-manager="transactionManager" specifies which transaction manager is to be configured. After specifying it, we need to configure the transaction method in this tag.

<!-- 配置事务管理器
        transaction-manager="transactionManager" 指定是配置哪个事务管理器
     -->
    <tx:advice id="myAdvice" transaction-manager="dataSourceTransactionManager">

    </tx:advice>

(3) Specify the business method

We need to add the tx:method tag to the tx:advice tag to tell Spring which methods are transaction methods (the transaction aspect will cut the transaction method according to our pointcut expression). Various parameters that can be used by simultaneous transactions can be declared in tx:method,

code show as below:

<!-- 配置事务管理器
        transaction-manager="transactionManager" 指定是配置哪个事务管理器
     -->
    <tx:advice id="myAdvice" transaction-manager="dataSourceTransactionManager">
        <!-- 事务属性 -->
        <tx:attributes>
            <!-- 指明哪些方法是事务方法,
                切入点表达式只是说,事务管理器要切入这些方法, -->
            <!-- 指定所有的方法都是事务方法 -->
            <tx:method name="*"/>
            <!-- 仅仅指定一个方法是事务方法,并且指定事务的属性 -->
            <tx:method name="buyBook" propagation="REQUIRED" timeout="-1"/>
            <!-- 表示所有以get开头的方法 -->
            <tx:method name="get*" read-only="true"/>
        </tx:attributes>
    </tx:advice>

At this point, the initial use of declarative transactions is considered complete, so when to use annotation-based transaction managers and when to use XML-based transaction managers?

Note: The correct one should be that both annotation-based and annotation-based are used. Important transactions use annotations, and unimportant transactions use configuration.

Do you think it's over here? But this is just the beginning, because the control of the transaction must be executed along with a variety of situations.

Third, the communication behavior of affairs

When a transaction method is called by another transaction method, you must specify how the transaction should be propagated. For example: The method may continue to run in an existing transaction, or it may start a new transaction and run in its own transaction.

transaction can be specified in the propagation attribute of the @Transactional annotation. Spring defines 7 types of propagation behaviors.

Their corresponding functions are shown in the following table:
image.png

Here I will talk about the two most commonly used communication behaviors:

1) REQUIRED: The current transaction and the previous large transaction

When the transaction uses REQUIRED, the attributes of the transaction are integrated in the big transaction, so the attributes applied to the method will not take effect separately, such as the timeout setting timeout.

When the transaction uses REQUIRES_NEW, the properties of the transaction can be adjusted,

2) REQUIRES_NEW: The current transaction always uses a new transaction. If there is already a transaction, the transaction will be suspended. After the current transaction commits and runs, it will continue to run the suspended transaction

Principle: REQUIRED is to pass the connection of the previous transaction to this method for use.

REQUIRES_NEW, this method directly uses the new connection

Fourth, the isolation level of the transaction

1. Database transaction concurrency issues

When we operate on the data in the database, there is often not only one person operating, that is to say, there may be concurrent execution of transactions, so since there is concurrent execution, there must be concurrent processing problems in this.

So what are the common transaction concurrency problems? Let's take the concurrent execution of two transactions Transaction01 and Transaction02 as an example to introduce:

(1) Dirty reading

The so-called dirty read means that a dirty data is read, and it is generally understood that the data read is invalid. **Such as the following operation example:
**

  1. Transaction01 changes the AGE value of a record from 20 to 30.
  2. Transaction02 reads the updated value of Transaction01: 30.
  3. Transaction01 was rolled back, and the AGE value was restored to 20.
  4. The 30 read by Transaction02 is an invalid value.

At this time, the transaction of Transaction02 has a dirty read.

(2), non-repeatable reading

From the meaning, we should also understand that when the same transaction reads data repeatedly, the data read twice is inconsistent.

Look at the following case:

  1. Transaction01 reads the AGE value as 20.
  2. Transaction02 changes the AGE value to 30.
  3. Transaction01 reads the AGE value of 30 again, which is inconsistent with the first read.

At this time, the data read twice by Transaction01 is inconsistent. When Transaction01 processes the transaction, there will be a situation where you don't know which data to use, which is non-repeatable reading.

(3), phantom reading

Does it feel amazing to hear this name? How come there are phantom readings? In fact, the meaning of phantom reading is that the data read twice is inconsistent.

Look at the following case:

  1. Transaction01 read part of the data in the STUDENT table.
  2. Transaction02 inserted a new row into the STUDENT table.
  3. When Transaction01 reads the STUDENT table, there are some extra rows.

Here, when Transaction01 read the data table for the second time, it was found that there was more data in the data table compared with the previous one. This is a phantom read.

2. Analysis of transaction isolation level

So how should we solve the three concurrency problems we mentioned above? The transaction isolation level is used here, because these problems are caused by concurrent execution, Therefore, the database system must have the ability to isolate and run various transactions concurrently, so that they will not affect each other and avoid various concurrency problems.

The degree of isolation of a transaction from other transactions is called the isolation level. SQL standard specifies a variety of transaction isolation levels. Different isolation levels correspond to different levels of interference. The higher the isolation level, the better the data consistency, but the weaker the concurrency.

There are four common isolation levels:

  1. uncommitted: READ UNCOMMITTED
    Allow Transaction01 to read the uncommitted changes of Transaction02.
  2. submitted: READ COMMITTED
    It is required that Transaction01 can only read the changes submitted by Transaction02.
  3. ③ Repeatable read: REPEATABLE READ
    Ensure that Transaction01 can read the same value from a field multiple times, that is, during the execution of Transaction01, other transactions are forbidden to update this field.
  4. : SERIALIZABLE

Ensure that Transaction01 can read the same row from a table multiple times. During the execution of Transaction01, prohibit other transactions from adding, updating, and deleting operations on this table. Any concurrency problems can be avoided, but the performance is very low.
But these isolation levels are not able to solve all the above concurrency problems. Their ability to solve concurrency problems is as follows:
image.png

At the same time, different databases have different levels of support for different isolation levels. Take MySQL and Oracle as examples:
image.png

3. Specify the isolation level for the method

We talked about the issue of transaction concurrency above, and also mentioned that the isolation level should be used to solve it, so the next step is how to increase the isolation level on the transaction method. There are two methods here.

(1) Specify the isolation level based on annotations

Specifying the transaction isolation level based on annotations can set the isolation level in the isolation attribute of @Transactional when @Transactional annotations declaratively manage transactions. In this way, the transaction method has the isolation level.

@Transactional(readOnly=true,isolation=Isolation.READ_UNCOMMITTED)
public int getPrice(String isbn) {
    return bookDao.getPriceFromBook(isbn);
}

(2). Specify isolation level based on XML

This method is that if you do not use annotations, you can declare the isolation level for the method in the XML configuration file. You can specify the isolation level in the isolation attribute of the <tx:method> element in the . as follows:

<tx:advice id="myAdvice" transaction-manager="dataSourceTransactionManager">
    <!-- 事务属性 -->
    <tx:attributes>
        <tx:method name="buyBook" propagation="REQUIRED" isolation="READ_COMMITTED"/>
    </tx:attributes>
</tx:advice>

Five, the exception that triggers the rollback of the transaction

We just said that rollback occurs when an error occurs, so can you specify that a rollback can only occur when a specific error occurs? Of course it is possible.

1. The default rollback exception

By default:

The system rolls back when it catches RuntimeException or Error, but does not roll back when it catches compile-time exception.

But now we can use a certain attribute to specify that it will only roll back when one or more errors occur.

2. Set a rollback under a specific exception

Setting the rollback under specific exceptions can also be declared in annotations or in XML,

(1) Set rollback through annotations

If you set the rollback through annotations, there are also two attributes under the @Transactional annotation:

rollbackFor attribute: specifies the type of exception that must be rolled back when encountered, which can be multiple
noRollbackFor attribute: specifies the type of exception that will not be rolled back when encountered, which can be multiple

When setting more than one, use braces {} to expand and separate them with commas.

as follows:

@Transactional(propagation=Propagation.REQUIRED,rollbackFor={IOException.class,SQLException.class},
noRollbackFor={ArithmeticException.class,NullPointerException.class})
public void updatePrice(String isbn,int price) {
    bookDao.updatePrice(isbn, price);
}

(2) Set rollback through XML

In Spring 2.x transaction notification, rollback rules can be specified in the <tx:method> element. If there is more than one exception, separate them with commas.

<!-- 配置事务管理器
transaction-manager="transactionManager" 指定是配置哪个事务管理器-->
<tx:advice id="myAdvice" transaction-manager="dataSourceTransactionManager">
<!-- 事务属性 -->
    <tx:attributes>
        <tx:method name="get*" read-only="true" rollback-for="java.io.IOException, java.sql.SQLException"
no-rollback-for="java.langArithmeticException"/>
    </tx:attributes>
</tx:advice>

Sixth, the timeout and read-only attributes of the transaction

Because transactions can acquire locks on rows and tables, long transactions can take up resources and have an impact on overall performance.

If a transaction only reads data but does not modify it, the database engine can optimize the transaction. Just use readOnly=true (interview test site, how to optimize the data acquisition?)

So here are two attributes:

timeout transaction attributes: transaction can be held before it is forced to roll back. This prevents long-running transactions from occupying resources. Use attribute timeout

Read-only transaction attributes: indicates that this transaction only reads data but does not update data, which can help the database engine optimize the transaction. Use attribute readOnly

Setting these two attributes can also be done through annotations or XML.

1. Annotation setting timeout and read-only

To set timeout and rollback through annotations, use the timeout attribute and readOnly attribute under the @Transactional annotation,

readOnly: read-only, the parameter is boolean; type, set the transaction as a read-only transaction (only query operations can be performed, operations that modify the database will not be executed) When optimizing transactions, you can use readOnly=true, which can increase Query speed, ignoring transaction-related operations

Timeout: Timeout, the parameter is int (in seconds), the transaction is automatically terminated and rolled back after the specified execution time is exceeded, and the parameter is "-1" (or less than 0), which means it will never time out.

out, an error will be reported: TransactionTimedOutException: Transaction timed out:

The example code is as follows:

@Transactional(timeout=3,readOnly=false,propagation=Propagation.REQUIRES_NEW)
public void buyBook(String username,String isbn){
    bookDao.updateStockFromBookStock(isbn);
    int price = bookDao.getPriceFromBook(isbn);
    bookDao.updateBalanceFromAccount(username, price);
    System.out.println("【" +username +  "】买书成功!");
}

2. XML settings timeout and read-only

In Spring 2.x transaction notification, timeout and read-only attributes can be specified in the <tx:method> element, and the two attributes timeout and readOnly are also used.

code show as below:

<!-- 配置事务管理器
transaction-manager="transactionManager" 指定是配置哪个事务管理器 -->
<tx:advice id="myAdvice" transaction-manager="dataSourceTransactionManager">
<!-- 事务属性 -->
    <tx:attributes>
        <tx:method name="get*" read-only="true"  timeout="3"/>
    </tx:attributes>
</tx:advice>

Seven, write at the end

Until here, the tutorial on using the declarative transaction manager in Spring is completely over, but there are still many details that we need to discover in the actual development.

Click to follow and learn about Huawei Cloud's fresh technology for the first time~

阅读 481

开发者之家
华为云开发者社区,提供全面深入的云计算前景分析、丰富的技术干货、程序样例,分享华为云前沿资讯动态...

华为云开发者社区,提供全面深入的云计算前景分析、丰富的技术干货、程序样例,分享华为云前沿资讯动态...

1.2k 声望
1.7k 粉丝
0 条评论

华为云开发者社区,提供全面深入的云计算前景分析、丰富的技术干货、程序样例,分享华为云前沿资讯动态...

1.2k 声望
1.7k 粉丝
文章目录
宣传栏