spring declarative transactions
What is a transaction?
If a group of operations forms a business, the group of operations will either succeed or fail. An operation to ensure the integrity of business operations.
Example: for example, transfer, deduction from Zhang San's account and addition from Li Si's account must be successful at the same time.
Spring JdbcTemplate
In spring, in order to operate JDBC more conveniently, an abstraction layer is defined on the basis of JDBC. The purpose of this design is to provide template methods for different types of JDBC operations. Each template method can control the whole process and allow to cover specific tasks in the process. In this way, flexibility can be retained as much as possible and the workload of database access can be minimized.
Introduce dependency
<!--spring Integrate third parties ORM This dependency will also be introduced at the same time spring-jdbc and spring-tx(affair)My bag and springIoc Foundation of jar package--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>5.2.6.RELEASE</version> </dependency> <!-- <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.6.RELEASE</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.2.6.RELEASE</version> <scope>compile</scope> </dependency>-->
Correspondence between driver and database version: https://dev.mysql.com/doc/connector-j/5.1/en/connector-j-versions.html
Configure connection pool and JdbcTemplate objects
<!--Configure scan--> <context:component-scan base-package="com.blog"/> <!--Import external profile--> <context:property-placeholder location="db.properties"/> <!--Configure connection pool objects--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="username" value="${mysql.username}"/> <property name="password" value="${mysql.password}"/> <property name="url" value="${mysql.url}"/> <property name="driverClassName" value="${mysql.driverClassName}"/> </bean> <!--to configure jdbc--> <bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <!--named argument jdbc Processing class--> <bean class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate" id="namedParameterJdbcTemplate"> <constructor-arg type="javax.sql.DataSource" ref="dataSource"/> </bean>
Test method display
ClassPathXmlApplicationContext context; @Before public void before(){ context = new ClassPathXmlApplicationContext("classpath:spring-ioc.xml"); } @Test public void test1(){ DruidDataSource dataSource = context.getBean("dataSource", DruidDataSource.class); System.out.println(dataSource); } /* * jdbcTemplate Connection test * */ @Test public void test2(){ JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class); Integer integer = jdbcTemplate.queryForObject("select count(1) from user", Integer.class); System.out.println(integer); } /* * jdbcTemplate Connection test * */ @Test public void test3(){ JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class); /* * If it is consistent with the database field * */ // User user = jdbcTemplate.queryForObject("select count(1) from User", new BeanPropertyRowMapper<>(User.class)); User o = jdbcTemplate.queryForObject("select * from user where id=1", (resultSet, i) -> { /*Get data from result set*/ User user = new User(); user.setBalance(resultSet.getInt("BALANCE")); user.setId(resultSet.getInt("ID")); user.setCardno(resultSet.getString("CARD_NO")); return user; }); System.out.println(o); } /* * Query entity list * */ @Test public void test4(){ JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class); List<User> userList = jdbcTemplate.query("select * from user", new RowMapper<User>() { @Override public User mapRow(ResultSet resultSet, int i) throws SQLException { /*Get data from result set*/ User user = new User(); user.setBalance(resultSet.getInt("BALANCE")); user.setId(resultSet.getInt("ID")); user.setCardno(resultSet.getString("CARD_NO")); return user; } }); System.out.println(userList); } /* * newly added * */ @Test public void test5(){ JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class); int i = jdbcTemplate.update("insert into user (REAL_NAME, CARD_NO, BALANCE) VALUES (?,?,?)", "Li Si", "133", "546"); System.out.println(i); } /* * modify * */ @Test public void test6(){ JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class); int i = jdbcTemplate.update("update user set BALANCE = ? where ID = 3", "546"); System.out.println(i); } /* * delete * */ @Test public void test7(){ JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class); int i = jdbcTemplate.update("delete from user where ID =?", 3); System.out.println(i); } /** * Named parameter processing NamedParameterJdbcTemplate */ @Test public void test08(){ NamedParameterJdbcTemplate jdbcTemplate = context.getBean(NamedParameterJdbcTemplate.class); Map<String,Object> map=new HashMap<>(); map.put("id",2); // Modify similar User user = jdbcTemplate.queryForObject("select * from user where ID = :id", map, new RowMapper<User>() { @Override public User mapRow(ResultSet resultSet, int i) throws SQLException { /*Get data from result set*/ User user = new User(); // If the queried data is 0, null is returned if(i == 0){ return null; } user.setBalance(resultSet.getInt("BALANCE")); user.setId(resultSet.getInt("ID")); user.setCardno(resultSet.getString("CARD_NO")); return user; } }); System.out.println(user); }
dao layer usage example
@Repository public class UserDaoImpl implements UserDao { private JdbcTemplate jdbcTemplate; /* * JdbcTemplate Thread safe, which means that you can use the same instance of JdbcTemplate in multiple Daos. Of course, it can also be defined as a private attribute in the same dao * official: https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#jdbc-JdbcTemplate-idioms * This is the official recommended usage. One dao corresponds to one JdbcTemplate * */ @Autowired public UserDaoImpl(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } }
Four transaction features ACID
Four characteristics of ACID
-
A (Atomicity): Atomicity refers to either success or failure in a group of business operations, or submission or rollback in a group of add, delete, modify and query businesses
-
C (Consistency): the data before and after the transaction should be consistent. Change from one Consistency state to another. For example, Zhang San gave Li Si 1000 yuan, but the total amount of Zhang San and Li Si remained unchanged.
-
I (Isolation): in the case of multithreading, the execution of one transaction should not be affected by another transaction.
-
D (Durability): it is permanent when the data is changed after the transaction is committed.
Summary: in terms of transaction control, there are two main categories:
Programming transaction
Adding transaction processing logic directly into the code may require explicitly calling transaction initiation, commit, rollback, for example, beginTransaction(), commit(), rollback(), and other transaction management related methods in the code
connetion.autoCommit(false);
connction.commint()
catch(){
connction.rollback();
}
Declarative transaction
Add annotations outside the method or define it directly in the configuration file to separate the transaction management code from the business method and realize the transaction management in a declarative way. spring's AOP can exactly accomplish this function: the fixed mode of transaction management code, as a crosscutting concern, is modularized through AOP method, so as to realize declarative transaction
spring declarative transaction usage
Configure the transaction manager and enable annotation based transaction control mode
<!--Configure the transaction manager. Since the underlying operations of the transaction are connected to start the transaction, commit rollback and other operations, it is necessary to configure the data source--> <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="dataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--Enable annotation based transaction control mode tx Namespace xmlns:tx="http://www.springframework.org/schema/tx If annotation configuration and xml All configuration, annotation first @EnableTransactionManagement Add to configuration class --> <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
Add the annotation @ Transactional on the required method
@Service public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Override public User getUser(){ return userDao.getUser(); } /* * It can be marked on the class (indicating that all methods are annotated) or on the method. If there is this annotation at the same time, the annotation on the method shall prevail * It is suggested to write in the method, and the strength is more detailed * It is recommended to download the business logic layer. * */ @Override @Transactional public void trans() { userDao.sub(); System.out.println("Zhang San deducted money to complete"); int i = 1/0; userDao.add(); } }
@Transactional transaction attribute configuration
isolation: Set the isolation level of transactions propagation: Propagation behavior of transactions noRollbackFor: Those abnormal transactions can not be rolled back noRollbackForClassName: The filled parameter is the full class name rollbackFor: Which abnormal transactions need to be rolled back rollbackForClassName: The filled parameter is the full class name readOnly: Sets whether a transaction is read-only timeout: When the transaction exceeds the specified execution time, it will be automatically terminated and rolled back,The unit is seconds
Isolation sets the isolation level of transactions
Used to deal with some problems under concurrent transactions
/* *Isolation Set the isolation level of transactions * Isolation.DEFAULT Use the default database isolation level -- default * Isolation.READ_UNCOMMITTED Read uncommitted * Isolation.READ_COMMITTED Read committed (non repeatable) * Isolation.REPEATABLE_READ Repeatable * Isolation.SERIALIZABLE Serial call * */ @Override @Transactional(isolation = Isolation.REPEATABLE_READ) public void trans() { //Modify, delete and other operations }
Transaction isolation level
Transaction isolation level | Dirty reading | Non repeatable reading | Unreal reading |
---|---|---|---|
Read uncommitted | yes | yes | yes |
Read committed | no | yes | yes |
Repeatable read | no | no | yes |
serializable | no | no | no |
Dirty read: a transaction has two operations a and b. operation a modifies the database. At this time, another transaction reads the data modified by operation a, and then operation b fails to execute, and the transaction is rolled back.
Non repeatable reading: transaction a reads the database data, while transaction b modifies the database data during this process, resulting in inconsistent transaction results between the two reads of transaction a. Moreover, it can be modified after inconsistency.
Repeatable reading: after transaction a reads the data, transaction b modifies the data read by transaction a, so it is the same for transaction a to obtain the data again.
Serial call: a and b transactions are not executed at the same time, but are forcibly controlled to be executed successively
Query the default isolation level of the database
select @@tx_isolation
Program test - repeatable - unreal:
reference resources:
https://blog.csdn.net/sanyuesan0000/article/details/90235335?utm_term=mysql%E5%B9%BB%E8%AF%BB%E7%9A%84%E5%BD%B1%E5%93%8D&utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2allsobaiduweb~default-0-90235335&spm=3001.4430
https://blog.csdn.net/qq_31930499/article/details/110393988
Test of unreal reading under repeatable reading
Mvcc -- > multi version concurrency control
MVCC is explained in high performance MySQL as follows
InnoDB MVCC is realized by saving two hidden columns after each row of records. These two columns, one saves the creation time of the row and the other saves the expiration time (or deletion time) of the row. Of course, what is stored is not the actual time value, but the system version number. Each time a new transaction is started, the system version number will increase automatically. The system version number at the beginning of the transaction will be used as the transaction version number to compare with the version number of each row of records queried. Let's take a look at how MVCC operates at the REPEATABLE READ isolation level.
SELECT
InnoDB will check each row of records according to the following two conditions:
a. InnoDB only looks for data rows whose version is earlier than the current transaction Version (that is, the system version number of the row is less than or equal to the system version number of the transaction), which can ensure that the rows read by the transaction either exist before the start of the transaction, or have been inserted or modified by the transaction itself.
b. The deleted version of the row is either undefined or greater than the current transaction version number. This ensures that the rows read by the transaction are not deleted before the transaction starts. Only records that meet the above two conditions can be returned as query results.
INSERT
InnoDB saves the current system version number as the line version number for each newly inserted line.
DELETE
InnoDB saves the current system version number for each deleted line as the line deletion ID.
UPDATE
InnoDB inserts a new line of records, saves the current system version number as the line version number, and saves the current system version number to the original line as the line deletion ID.
Here is the test process
Transaction a | Transaction b | |
---|---|---|
T1 | select * from user | |
T2 | update user set balance=balance-200 | |
T3 | update user set balance=balance-200 | insert into user (REAL_NAME, CARD_NO, BALANCE) VALUES (?,?,?) "," Li Si "," 133 "," 546 " |
T4 | commit | |
T5 | select * from user | |
T6 | commit | |
T7 | ||
Understanding of test results: transaction a executes the update operation after query. Since the same data is updated as transaction b, and transaction b executes first, transaction a is waiting because of the lock problem, so transaction b can be modified only after it is submitted. At this time, the modified and newly added data of transaction b will be marked with the corresponding version number. At this time, transaction a modifies the data, and the submitted data of transaction b will be obtained due to the current read operation during modification, After modification, transaction b updates the version number of all data again, including the one just added by transaction b, so transaction a will find one more one again.
Propagation characteristics of propagation transactions
The propagation characteristic of transaction refers to that when a transaction method is called by another transaction method, the transaction method
How should it be carried out?
It is hoped that if there is an external transaction, the external transaction will be used, and if there is no external transaction, the transaction will be started by itself
The transaction opened on a is called the current transaction, and it is also called an external transaction relative to b and c
a(){ b(); c(); }
Transaction propagation behavior | External transaction does not exist | External transaction | Usage scenario |
---|---|---|---|
REQUIRED (default) | Start a new transaction | Integration into external transactions | @Transactional(propagation = Propagation.REQUIRED) is applicable to addition, deletion, modification and query |
SUPPORTS | Do not open a new transaction | Integration into external transactions | @Transactional(propagation = Propagation.SUPPORTS) applies to queries |
REQUIRES_NEW | Start a new transaction | Suspend external transactions and create new transactions | @Transactional (propagation = propagation. Requirements_new) is applicable to internal transactions and external transactions without business association, such as logs |
NOT_SUPPORTED | Do not open a new transaction | Suspend external transactions | @Transactional (promotion =promotion.not\u supported) not commonly used |
NEVER | Do not open a new transaction | Throw exception | @Transactional(propagation = Propagation.NEVER) is not commonly used |
MANDATORY | Throw exception | Integration into external transactions | @Transactional(propagation = Propagation.MANDATORY) is not commonly used |
Timeout transaction execution timeout
Specifies the maximum time (in seconds) for a transaction to wait
When the current transaction accesses data, the data that may be accessed is locked by other data. At this time, the transaction must wait. If the waiting time is too long, it will cause poor experience for users.
@Transactional(timeout = 2)// In seconds
Set transaction read only (readOnly)
readonly: it will only be set in the business method of the query
connection.setReadOnly(true) notifies the database that the current database operation is read-only, and the database will optimize the current read-only operation accordingly
Usage scenario:
If you execute a single query statement at a time, it is not necessary to enable transaction support, and the database defaults
Support read consistency during SQL execution;
If you execute multiple query statements at one time, such as statistical query and report query, in this field
In this scenario, the overall read consistency of multiple SQL queries must be guaranteed. Otherwise, after the previous SQL query, the last SQL query
Before an SQL query, if the data is changed by other users, the overall statistical query will read data
In this case, transaction support should be enabled (such as setting non repeatability and phantom reading level)
Don't).
Exception attribute
Set the exceptions of the current transaction to rollback or commit.
By default, the rollback strategy is adopted for RuntimeException and its subclasses.
The default policy for Exception and its subclasses is submission.
1. Set which exceptions are not rolled back (noRollbackFor)
2. Set which exception rollbacks (rollbackFor)
@Transactional(timeout = 3,rollbackFor = {FileNotFoundException.class})
Use in practice
If the current business method is a group of add, modify and delete, you can set the transaction in this way
@Transactional
If the current business method is a group of queries, you can set transactions in this way
@Transactionl(readOnly=true)
If the current business method is a single query, you can set the transaction in this way
@Transactionl(propagation=propagation.SUPPORTS ,readOnly=true)
xml based usage
<?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 https://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 https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--Configure scan--> <context:component-scan base-package="com.blog"/> <!--Import external profile--> <context:property-placeholder location="db.properties"/> <!--Configure connection pool objects--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="username" value="${mysql.username}"/> <property name="password" value="${mysql.password}"/> <property name="url" value="${mysql.url}"/> <property name="driverClassName" value="${mysql.driverClassName}"/> </bean> <!--to configure jdbc--> <bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <!--named argument jdbc Processing class--> <bean class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate" id="namedParameterJdbcTemplate"> <constructor-arg type="javax.sql.DataSource" ref="dataSource"/> </bean> <!--Configure the transaction manager. Since the underlying operations of the transaction are connected to start the transaction, commit rollback and other operations, it is necessary to configure the data source--> <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="dataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--Enable annotation based transaction control mode tx Namespace xmlns:tx="http://www.springframework.org/schema/tx --> <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/> <!--Declarative transaction passed aop Implementation: in different positions of the method, start, roll back and commit transactions through the connection object --> <aop:config> <!--Match all classes and methods in the business implementation layer--> <aop:pointcut id="transactionCut" expression="execution(* com.blog.service.impl.*.*(..))"/> <aop:advisor advice-ref="myAdvice" pointcut-ref="transactionCut"/> </aop:config> <!--Specify the methods to which the pointcut matches and the transactions to declare--> <tx:advice id="myAdvice" transaction-manager="dataSourceTransactionManager"> <tx:attributes> <!-- wildcard--> <tx:method name="update*"/> <tx:method name="delete*"/> <tx:method name="add*"/> <!--to configure get The method at the beginning is read-only, and the transaction will not be opened when the current transaction does not exist, and will be integrated into the current transaction when it exists--> <tx:method name="get" read-only="true" propagation="SUPPORTS"/> </tx:attributes> </tx:advice> </beans>