Note source: Silicon Valley Spring Framework video tutorial (Spring 5 source level explanation)

JdbcTemplate with declarative transactions

1, the JdbcTemplate

1.1 overview,

We have already studied the Core Container part of Spring and the aspect oriented programming part such as AOP and Aspects, followed by Data Access/Integration, which is the Data Access and Integration part

Spring can be used alone or integrated with other frameworks such as Hibernate, MyBatis, etc. In addition, JDBC has also been encapsulated, that is, the JdbcTemplate of this chapter, which can be used to easily add, delete, change, check and other operations on the database

To sum up:

  • JdbcTemplateThe Spring framework pairJDBCTechnology for secondary encapsulation of templates, can simplify the operation of the database

1.2. Preparation

Steps to preview

  • 1) Introduce correlationjarpackage
  • 2) Spring configuration file configurationDruidConnection Pool Information
  • 3) configurationJdbcTemplateObject, injectiondataSource
  • 4) Create Service and Dao classes and inject them into Dao classesJdbcTemplateobject

Detailed operation

  • 1) Introduce correlationjarPackages (or dependencies)
    • druid
    • mysql-connector-java
    • spring-jdbc
    • spring-orm
    • spring-tx

  • 2) Spring configuration file configurationDruidConnection Pool Information
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${mysql.driverClassName}"/>
    <property name="url" value="${mysql.url}"/>
    <property name="username" value="${mysql.username}"/>
    <property name="password" value="${mysql.password}"/>
</bean>
Copy the code

Keep the jdbc.properties configuration information from the previous section, but modify it slightly

mysql.driverClassName=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql:///book_db
mysql.username=root
mysql.password=root
Copy the code
  • 3) configurationJdbcTemplateObject, injectiondataSource
<! - configured JdbcTemplate -- -- >
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <! DataSource -->
    <property name="dataSource" ref="dataSource"></property>
</bean>
Copy the code

Why use property injection?

The JdbcTemplate calls the setDataSource() method, although it has the DataSource argument constructor

This method is defined in its parent class

  • 4) Create Service and Dao classes and inject them into Dao classesJdbcTemplateobject

Dao class

public interface BookDao {}@Repository
public class BookDaoImpl implements BookDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;
}
Copy the code

The Service class

@Service
public class BookService {
    @Autowired
    private BookDao bookDao;
}
Copy the code

Don’t forget to turn on annotation scanning

<! -- Enable annotation scan -->
<context:component-scan base-package="com.vectorx.spring5.s15_jdbctemplate"/>
Copy the code

Overall configuration file structure


      
<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"
       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">

    <! -- Enable annotation scan -->
    <context:component-scan base-package="com.vectorx.spring5.s15_jdbctemplate"/>

    <! - configure the dataSource - >
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${mysql.driverClassName}"/>
        <property name="url" value="${mysql.url}"/>
        <property name="username" value="${mysql.username}"/>
        <property name="password" value="${mysql.password}"/>
    </bean>

    <! - configured JdbcTemplate -- -- >
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <! DataSource -->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
</beans>
Copy the code

1.3. Add operations

Steps to preview

  • 1) Create a databaset_bookThe entity object corresponding to the table
  • 2) Write Service and Dao code, addAdd booksFunction logic of
  • 3) Code testing

Detailed operation

  • 1) Create a databaset_bookThe entity object corresponding to the table

public class Book {
    private String bid;
    private String bname;
    private String bstatus;

    public String getBid(a) {
        return bid;
    }
    public void setBid(String bid) {
        this.bid = bid;
    }
    public String getBname(a) {
        return bname;
    }
    public void setBname(String bname) {
        this.bname = bname;
    }
    public String getBstatus(a) {
        return bstatus;
    }
    public void setBstatus(String bstatus) {
        this.bstatus = bstatus; }}Copy the code
  • 2) Write Service and Dao code, addAdd booksFunction logic of

Service class: Add the addBook() method

@Service
public class BookService {
    @Autowired
    private BookDao bookDao;

    public int addBook(Book book) {
        returnbookDao.add(book); }}Copy the code

Dao class: Insert by operating on the UPDATE () method of the JdbcTemplate object with two parameters

  • The first parametersql: Writes the corresponding to the inserted datasqlStatement, wildcard characters can be used?Do a placeholder
  • Second parameterargs: Variable parameter list, set the parameter values corresponding to the placeholders
public interface BookDao {
    int add(Book book);
}
@Repository
public class BookDaoImpl implements BookDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;
 
    @Override
    public int add(Book book) {
        // Manipulate the JdbcTemplate object, adding it using the update method
        String sql = "insert into t_book(bid,bname,bstatus) values(? ,? ,?) ";
        Object[] args = {book.getBid(), book.getBname(), book.getBstatus()};
        returnjdbcTemplate.update(sql, args); }}Copy the code
  • 3) Code testing
ApplicationContext context = new ClassPathXmlApplicationContext("bean13.xml");
BookService bookService = context.getBean("bookService", BookService.class);

// simulate new books
Book book = new Book();
book.setBid("1");
book.setBname("Spring JdbcTemplate");
book.setBstatus("1");
int result = bookService.addBook(book);
System.out.println(result);
Copy the code

The test results

Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessaryMarch 06, 2022 10:25:49 PMcom.alibaba.druid.pool.DruidDataSource infoInformation:{dataSource-1} inited
1
Copy the code

Refresh the T_book table data in the database and check whether the database is successfully inserted

As you can see, a new entry has been successfully added to the table

1.4. Modification and Deletion

Modify, delete and add operation code logic is basically the same

BookService class: Add updateBook() and deleteBook() methods

/ / modify
public int updateBook(Book book) {
    return bookDao.update(book);
}
/ / delete
public int deleteBook(String id) {
    return bookDao.delete(id);
}
Copy the code

BookDao class: Add update() and Delete () methods

/ / modify
int update(Book book);
/ / delete
int delete(String id);
Copy the code

BookDaoImpl class: Implements update() and delete() methods

/ / modify
@Override
public int update(Book book) {
    String sql = "update t_book set bname=? ,bstatus=? where bid=?";
    Object[] args = {book.getBname(), book.getBstatus(), book.getBid()};
    return jdbcTemplate.update(sql, args);
}
/ / delete
@Override
public int delete(String id) {
    String sql = "delete from t_book where bid=? ";
    return jdbcTemplate.update(sql, id);
}
Copy the code

Test to modify

// Modify the book information
Book book = new Book();
book.setBid("1");
book.setBname("JdbcTemplate");
book.setBstatus("update");
int result2 = bookService.updateBook(book);
System.out.println(result2);
Copy the code

The test results

Test to delete

// Delete the book
int result3 = bookService.deleteBook("1");
System.out.println(result3);
Copy the code

The test results

1.5. Query operation

Three query operations are demonstrated here:

  • 1) The query returns a value
  • 2) Query returned objects
  • 3) Query return collection

To demonstrate this, you need to add two pieces of data to the T_book table of the database

Then we will first complete the code, and finally make further analysis

Code implementation

BookService class: Add the findCount(), findById(), and findAll() methods

// The search returns a value
public int findCount(a) {
    return bookDao.selectCount();
}
// Find the returned object
public Book findById(String id) {
    return bookDao.selectById(id);
}
// Find the return collection
public List<Book> findAll(a) {
    return bookDao.selectAll();
}
Copy the code

BookDao class: Add selectCount(), selectById(), and selectAll() methods

// The search returns a value
int selectCount(a);
// Find the returned object
Book selectById(String id);
// Find the return collection
List<Book> selectAll(a);
Copy the code

BookDaoImpl class: implements selectCount(), selectById(), and selectAll() methods

// The search returns a value
@Override
public int selectCount(a) {
    String sql = "select count(0) from t_book";
    return jdbcTemplate.queryForObject(sql, Integer.class);
}
// Find the returned object
@Override
public Book selectById(String id) {
    String sql = "select * from t_book where bid=?";
    return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Book.class), id);
}
// Find the return collection
@Override
public List<Book> selectAll(a) {
    String sql = "select * from t_book where 1=1";
    return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Book.class));
}
Copy the code

The test code

int count = bookService.findCount();
System.out.println(count);
Book book = bookService.findById("1");
System.out.println(book);
List<Book> bookList = bookService.findAll();
System.out.println(bookList);
Copy the code

The test results

2
Book{bid='1', bname='Spring', bstatus='add'}
[Book{bid='1', bname='Spring', bstatus='add'}, Book{bid='2', bname='SpringMVC', bstatus='add'}]
Copy the code

The code analysis

The queryForObject() and Query () methods are used in the code logic above

jdbcTemplate.queryForObject(sql, Integer.class);
jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Book.class), id);
jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Book.class));
Copy the code

Each corresponds to three methods in the JdbcTemplate

public <T> T queryForObject(String sql, Class<T> requiredType);
public <T> T queryForObject(String sql, RowMapper<T> rowMapper, Object... args);
public <T> List<T> query(String sql, RowMapper<T> rowMapper);
Copy the code

There are two parameters of interest, Class

requiredType and RowMapper

RowMapper

  • Class<T> requiredType: returns a valueClasstype
  • RowMapper<T> rowMapper: is an interface that returns different types of data that can be encapsulated using its implementation classes. There are many implementation classes, and we can choose to use them because we need to return a database entity objectBeanPropertyRowMapper

QueryForObject (String SQL, RowMapper

RowMapper, Object… Args) and Query (String SQL, RowMapper

RowMapper)

The difference is that

  • queryForObjectReturn an object
  • queryReturn a collection

1.6. Batch operation

JdbcTemplate provides batchUpdate() for batch operations, such as batch add, batch modify, batch delete, etc. The code implementation is similar, so we can quickly implement the code

Code implementation

BookService class: Add batchAddBook(), batchUpdateBook(), and batchDelBook() methods

// Batch add
public void batchAddBook(List<Object[]> bookList) {
    bookDao.batchAdd(bookList);
}
// Batch change
public void batchUpdateBook(List<Object[]> bookList) {
    bookDao.batchUpdate(bookList);
}
// Batch delete
public void batchDelBook(List<Object[]> bookList) {
    bookDao.batchDel(bookList);
}
Copy the code

BookDao class: Add batchAdd(), batchUpdate(), and batchDel() methods

// Batch add
void batchAdd(List<Object[]> bookList);
// Batch change
void batchUpdate(List<Object[]> bookList);
// Batch delete
void batchDel(List<Object[]> bookList);
Copy the code

BookDaoImpl class: implements the batchAdd(), batchUpdate(), and batchDel() methods

// Batch add
@Override
public void batchAdd(List<Object[]> bookList) {
    String sql = "insert into t_book(bid,bname,bstatus) values(? ,? ,?) ";
    extractBatch(sql, bookList);
}
// Batch change
@Override
public void batchUpdate(List<Object[]> bookList) {
    String sql = "update t_book set bname=? ,bstatus=? where bid=?";
    extractBatch(sql, bookList);
}
// Batch delete
@Override
public void batchDel(List<Object[]> bookList) {
    String sql = "delete from t_book where bid=? ";
    extractBatch(sql, bookList);
}
private void extractBatch(String sql, List<Object[]> bookList,) {
    int[] ints = jdbcTemplate.batchUpdate(sql, bookList);
    System.out.println(ints);
}
Copy the code

The test code

Test batch add

// Batch add
List<Object[]> bookList = new ArrayList<>();
Object[] book1 = {"3"."Java"."batchAdd"};
Object[] book2 = {"4"."Python"."batchAdd"};
Object[] book3 = {"5"."C#"."batchAdd"};
bookList.add(book1);
bookList.add(book2);
bookList.add(book3);
bookService.batchAddBook(bookList);	
Copy the code

The test results

Test batch modification

// Batch change
List<Object[]> bookList = new ArrayList<>();
Object[] book1 = {"Java++"."batchUpdate"."3"};
Object[] book2 = {"Python++"."batchUpdate"."4"};
Object[] book3 = {"C#++"."batchUpdate"."5"};
bookList.add(book1);
bookList.add(book2);
bookList.add(book3);
bookService.batchUpdateBook(bookList);
Copy the code

The test results

Test Batch Deletion

// Batch delete
List<Object[]> bookList = new ArrayList<>();
Object[] book1 = {"3"};
Object[] book2 = {"4"};
bookList.add(book1);
bookList.add(book2);
bookService.batchDelBook(bookList);
Copy the code

The test results

As you can see, the above tests are completely in line with our expectations

summary

A brief summary of the various methods used by JdbcTemplate to manipulate a database

  • Add, modify, delete operations:update()methods
  • Query operation:queryForObject()andquery()Method, focusing on two parameters:
    • Class<T> requiredType: returns a valueClasstype
    • RowMapper<T> rowMapper: interface, concrete implementation classBeanPropertyRowMapperEncapsulate object entities
  • Batch operation:batchUpdate()methods

2, transaction

2.1. Transaction concept

  • 1) Transaction is the most basic unit of database operation, is a logical group of operations. This set of operations will either all succeed or all fail (if one operation fails, all operations fail)
  • 2) Typical scenario: Bank transfer. Lucy transfers 100 yuan to Mary, Lucy loses 100 yuan and Mary loses 100 yuan. If there are any problems in the transfer process, neither side will get more or less money, and the transfer will not be successful

2.2 Four Characteristics of Transaction (ACID)

  • Atomicity: All operations in a transaction either succeed or fail. The whole process is indivisible
  • Consistency: The total amount remains the same before and after a transaction operation
  • Isolation: When multiple transactions operate, they do not affect each other
  • Durability: Data in database tables really changes only after the transaction is finally committed

2.3. Set up transaction operation environment

As we know, JavaEE consists of three layers: presentation layer (Web layer), business logic layer (Service layer) and data access layer (DAO layer).

  • webLayer: Interacts with the client
  • serviceLayer: Handles the business logic
  • daoLayer: Interacts with the database

Therefore, we built the operating environment according to the typical three-tier architecture, but at present we only focus on the Service and Dao two layers

Let’s take a bank transfer as an example, because the whole transfer operation consists of two operations: the outgoing operation and the incoming operation

An overview of the process

  • 1) Create a database table structure and add a few records
  • 2) createServiceandDaoClass to complete object creation and relationship injection
  • 3)DaoTo create two methods: accounting method, accounting method;ServiceTo create a transfer method

The process,

**1) ** Create a database table structure and add a few records

# Build predicative sentencescreate table t_account
(
    id       varchar(20) not null,
    username varchar(50) null,
    amount   int         null.constraint transfer_record_pk
        primarykey (id) ); # add statementINSERT INTO book_db.t_account (id, username, amount) VALUES ('1'.'Lucy'.1000);
INSERT INTO book_db.t_account (id, username, amount) VALUES ('2'.'Mary'.1000);
Copy the code

Add finish effect

**2) ** Create Service and Dao classes to complete object creation and relationship injection

The Dao is injected into the Service, the JdbcTemplate is injected into the Dao, and the JdbcTemplate is injected into the DataSource

The Service and the Dao class

public interface TransferRecordDao {}@Repository
public class TransferRecordDaoImpl implements TransferRecordDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;
}
@Service
public class TransferRecordService {
    @Autowired
    private TransferRecordDao transferRecordDao;
}
Copy the code

Spring configuration file


      
<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"
       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">
    <! -- Enable annotation scan -->
    <context:component-scan base-package="com.vectorx.spring5.s16_transaction"/>
    <! - configure the dataSource - >
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${mysql.driverClassName}"/>
        <property name="url" value="${mysql.url}"/>
        <property name="username" value="${mysql.username}"/>
        <property name="password" value="${mysql.password}"/>
    </bean>
    <! - configured JdbcTemplate -- -- >
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <! DataSource -->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
</beans>
Copy the code

3) Create two methods in Dao: accounting method and accounting method; Service to create a transfer method

  • DaoIs responsible for database operations, so you need to create two methods: an accounting method and an accounting method
public interface TransferRecordDao {
    void transferOut(int amount, String username);
    void transferIn(int amount, String username);
}
@Repository
public class TransferRecordDaoImpl implements TransferRecordDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void transferOut(int amount, String username) {
        String sql = "update t_account set amount=amount-? where username=?";
        Object[] args = {amount, username};
        jdbcTemplate.update(sql, args);
    }
    @Override
    public void transferIn(int amount, String username) {
        String sql = "update t_account set amount=amount+? where username=?"; Object[] args = {amount, username}; jdbcTemplate.update(sql, args); }}Copy the code
  • ServiceIs responsible for business operations, so you need to create a method to callDaoTwo methods in
@Service
public class TransferRecordService {
    @Autowired
    private TransferRecordDao transferRecordDao;

    public void transferAccounts(int amount, String fromUser, String toUser) { transferRecordDao.transferOut(amount, fromUser); transferRecordDao.transferIn(amount, toUser); }}Copy the code

The test code

ApplicationContext context = new ClassPathXmlApplicationContext("bean14.xml");
TransferRecordService transferRecordService = context.getBean("transferRecordService", TransferRecordService.class);
transferRecordService.transferAccounts(100."Lucy"."Mary");
Copy the code

The test results

As you can see, the transfer went through on schedule. But is there really nothing wrong?

2.4. Introduce transaction scenarios

We simulated the network abnormality occurred during the transfer process and modified the transfer method in the TransferRecordService

public void transferAccounts(int amount, String fromUser, String toUser) {
    transferRecordDao.transferOut(amount, fromUser);
    // The operation is interrupted due to an analog network exception
    int i = 10 / 0;
    transferRecordDao.transferIn(amount, toUser);
}
Copy the code

To see the changes more clearly and visually, we restore the table data to its original state

The expectation is that the transfer should fail, that is, there should be no change in the accounts of both parties. Can it really be as we expect?

We execute the test method and throw the exception as expected

Exception in thread "main" java.lang.ArithmeticException: / by zero
    at com.vectorx.spring5.s16_transaction.service.TransferRecordService.transferAccounts(TransferRecordService.java:15)
    at com.vectorx.spring5.s16_transaction.TestTransfer.main(TestTransfer.java:11)
Copy the code

Does the data sheet change as expected?

We found that although Lucy successfully transferred 100 yuan, Mary did not successfully transfer 100 yuan. From a practical point of view, this problem is very serious!!

From the perspective of the transaction, the transfer operation does not follow the atomicity and consistency of the transaction, i.e., it does not achieve “either success or failure”, nor does it achieve “the same amount before and after operation”.

In summary, we need to introduce transactions

2.5. Basic transaction operations

The basic operation of a transaction is as follows

  • Step1 start a transaction
  • Step2. Implement the business logic
  • Step3. If there is no exception, the transaction is submitted
  • Step4. If an exception occurs, the transaction is rolled back

The general implementation of a transaction is as follows

try {
    // start a transaction
    // Step2 implement the business logic
    transferRecordDao.transferOut(amount, fromUser);
    // The operation is interrupted due to an analog network exception
    int i = 10 / 0;
    transferRecordDao.transferIn(amount, toUser);
    // If there is no exception, commit the transaction
} catch (Exception e) {
    // if no exception occurs, rollback the transaction
}
Copy the code

However, there is a more convenient way to implement transactions in the Spring framework. “If you want to know what happens next, listen next time.”

summary

This summary is the main content of the key points

  • Basic concepts of transactions: The basic unit of database operations, a logical set of operations that either all succeed or all fail
  • The four basic properties of transactions are ACID, which stands for atomicity, consistency, isolation, and persistence

Declarative transactions

3.1 Spring Transaction Management

Transactions are typically added to the Service layer (business logic layer) in a three-tier structure

There are two ways to conduct transaction management operations in Spring: programmatic and declarative

  • Programmatic transaction Management (not recommended) : The generic implementation of the above transactions is a typical programmatic transaction management implementation. But although this way is not good, but we still need to have a certain understanding, know that there is such a process. This method is not recommended for the following reasons
    • 1) Inconvenient implementation
    • 2) Resulting in code bloat
    • 3) It is troublesome to maintain
  • Declarative transaction management (recommended) : The underlying principle isAOP, is to extend the function of the code without changing the original code. There are two ways to do this
    • Annotation-based approach (recommended)
    • Based on XML configuration files

Spring Transaction Management API

Provides an interface that represents the transaction manager and has different implementation classes for different frameworks

The main control

  • PlatformTransactionManagerInterface: the transaction manager, which has several different abstract and concrete implementation classes to satisfy different frameworks
  • DataSourceTrasactionManagerImplementation class:JdbcTemplateandMyBatisThe framework uses it
  • HibernateTransactionManagerImplementation class:HibernateThe framework uses it

3.3 declarative transactions (annotations)

  • 1) Configure the transaction manager in the Spring configuration file: configureDataSourceTransactionManagerObject creation
<! -- Configure transaction manager -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>
Copy the code
  • 2) Enable transactions in the Spring configuration file: importxmlns:txNamespace, and configure<tx:annotation-driven>Tag to turn on transaction annotations
<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"
       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">
    <! Other configuration information is omitted.
    <! -- Enable transaction annotations -->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
Copy the code
  • 3) inService(orServiceAdd transaction annotations to the@Transactional
@Service
@Transactional
public class TransferRecordService {
    / /...
}
Copy the code

First restore the data table information to its original state

Refresh the data table after testing the code

This time the data did not change, indicating that the transaction rolled back, as we expected

3.4 transaction parameters

Add the @Transactional annotation to the Service class to configure Transactional parameters

The main parameters are as follows

  • propagation: Transaction propagation behavior
  • isolation: Transaction isolation level
  • timeout: Timeout period
  • readOnly: Read-only
  • rollbackFor: roll back
  • noRollbackFor: don’t rolled back

3.5. Communication behavior

  • Transaction propagation behavior: How are transactions managed when multiple transaction methods are invoked directly
  • Transaction methods: Operations that make changes to table data

The propagation behavior of a transaction can be specified with propagation properties. There are seven types of propagation behaviors defined in the Spring framework:

Propagation properties describe
REQUIRED If a transaction is running, the current method runs within that transaction; Otherwise, a new transaction is started and runs within its own transaction
REQUIRED_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_SOPPORTED The current method should not run inside a transaction, and if there is a running transaction, suspend it
MANDATORY The current method must run inside a transaction and throw an exception if no transaction is running
NEVER The current method should not be running in a transaction; if there is a running transaction, throw an exception
NESTED If a transaction is running, the current method should run within a nested transaction of that transaction; Otherwise, a new transaction is started and runs within its own transaction

For example: Define two methods add() and update()

@Transactional
public void add(a){
    // Call the update method
    update();
}
public void update(a){
    // do something
}
Copy the code

According to different transmission attributes, there can be the following explanation

  • REQUIRED
    • ifadd()The method itself has transactions calledupdate()After the method,update()With the currentadd()Transactions within a method;
    • ifadd()The method itself has no transaction, calledupdate()Method, a new transaction is created
  • REQUIRED_NEW
    • useadd()The method callupdate()Method, no matteradd()Create a new transaction if there is one

Code implementation

@Service
@Transactional(propagation = Propagation.REQUIRED)
public class TransferRecordService {
    / /...
}
Copy the code

Is equivalent to

@Service
@Transactional
public class TransferRecordService {
    / /...
}
Copy the code

The default is not to write, and the transaction propagation behavior is REQUIRED

3.6. Isolation level

Among the four characteristics of transactions, isolation refers to the fact that multiple transactions do not affect each other. Regardless of isolation, a number of problems arise when concurrency occurs

There are three typical “read” problems: dirty reads, unrepeatable reads, and imaginary (phantom) reads

  • Dirty read: an uncommitted transaction reads data modified by another uncommitted transaction

  • Unrepeatable read: an uncommitted transaction reads data modified by another committed transaction (not a problem, just a symptom)

  • Phantom read: an uncommitted transaction reads data added by another committed transaction

You can solve the above “read” problem by setting the isolation level

Transaction isolation level Dirty read Unrepeatable read Phantom read
READ UNCOMMITTED(Read unsubmitted) Square root Square root Square root
READ COMMITTED(Read submitted) x Square root Square root
REPEATABLE READ(Repeatable) x x Square root
SERIALIZABLE(Serialization) x x x

Code implementation

@Service
@Transactional(isolation = Isolation.REPEATABLE_READ)
public class TransferRecordService {
    / /...
}
Copy the code

REPEATABLE READ = REPEATABLE READ = REPEATABLE READ

3.7 Other Parameters

  • timeout: Sets the transaction timeout period. A transaction needs to commit within a certain amount of time. If the transaction does not commit within the specified time, the transaction is rolled back. The default value is- 1To set the time in seconds
@Service
@Transactional(timeout = 5)
public class TransferRecordService {
    @Autowired
    private TransferRecordDao transferRecordDao;

    public void transferAccounts(int amount, String fromUser, String toUser) {
        transferRecordDao.transferOut(amount, fromUser);
        // Emulation timed out
        try {
            Thread.sleep(6000);
        } catch(InterruptedException e) { e.printStackTrace(); } transferRecordDao.transferIn(amount, toUser); }}Copy the code

Set the timeout, execute the test code, the log information throws TransactionTimedOutException namely transaction timeout exception

Exception in thread "main" org.springframework.transaction.TransactionTimedOutException: Transaction timed out: deadline was Wed Mar 09 21:30:33 CST 2022
    at org.springframework.transaction.support.ResourceHolderSupport.checkTransactionTimeout(ResourceHolderSupport.java:155)
    at org.springframework.transaction.support.ResourceHolderSupport.getTimeToLiveInMillis(ResourceHolderSupport.java:144)
    at org.springframework.transaction.support.ResourceHolderSupport.getTimeToLiveInSeconds(ResourceHolderSupport.java:128)
    at org.springframework.jdbc.datasource.DataSourceUtils.applyTimeout(DataSourceUtils.java:341)...Copy the code
  • readOnly: Indicates whether it is read-only.
    • The default value isfalse, indicates that read and write operations are allowed, and operations such as add, delete, modify, and query can be performed.
    • Can be set totrue, indicates that only read operations are allowed, that is, only query operations can be performed
@Service
@Transactional(readOnly = true)
public class TransferRecordService {
    / /...
}
Copy the code

Set only reflection, execute the test code, the log information throws TransientDataAccessResourceException namely transient abnormal data access resources, as well as SQLException, and points out that the connection is read-only, query in data modification is not allowed”

Exception in thread "main"org.springframework.dao.TransientDataAccessResourceException: PreparedStatementCallback; SQL [update t_account set amount=amount-? where username=?] ; Connection is read-only. Queries leading to data modification are not allowed; nested exception is java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed ... Caused by: java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed ...Copy the code
  • rollbackFor: Sets the exceptions to be rolled back
@Service
@Transactional(rollbackFor = ArithmeticException.class)
public class TransferRecordService {
    @Autowired
    private TransferRecordDao transferRecordDao;

    public void transferAccounts(int amount, String fromUser, String toUser) {
        transferRecordDao.transferOut(amount, fromUser);
        // The operation is interrupted due to an analog network exception
        int i = 10 / 0; transferRecordDao.transferIn(amount, toUser); }}Copy the code

The above code shows that the rollback of the transaction is performed only if the exception thrown is ArithmeticException

Running the test code throws a ArithmeticException in the background, so it rolls back and the transfer fails

The data in the database will not change at all

  • noRollbackFor: Sets the exceptions that do not roll back
@Service
@Transactional(noRollbackFor = ArithmeticException.class)
public class TransferRecordService {
    @Autowired
    private TransferRecordDao transferRecordDao;

    public void transferAccounts(int amount, String fromUser, String toUser) {
        transferRecordDao.transferOut(amount, fromUser);
        // The operation is interrupted due to an analog network exception
        int i = 10 / 0; transferRecordDao.transferIn(amount, toUser); }}Copy the code

Since the noRollbackFor = ArithmeticException. Class, it indicates that the selling ArithmeticException exception is not rolled back

Running the test code throws a ArithmeticException in the background, but it does not roll back. Only the debit operation will succeed in the transfer transaction

Exception in thread "main" java.lang.ArithmeticException: / by zero
Copy the code

Now the data in the database will look like this (which is obviously not what we want)

3.8 Declarative Transactions (XML mode)

  • Step1. Configure the transaction manager
<! -- Configure transaction manager -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>
Copy the code
  • Step2. Configure transaction notification
<! Configure transaction notification -->
<tx:advice id="txAdvice">
    <tx:attributes>
        <tx:method name="transferAccounts" propagation="REQUIRED" isolation="REPEATABLE_READ" read-only="false"
                   timeout="5" rollback-for="java.lang.ArithmeticException"/>
    </tx:attributes>
</tx:advice>
Copy the code
  • Step3. Configure pointcuts and facets
<! Configure pointcuts and facets -->
<aop:config>
    <aop:pointcut id="p"
                  expression="execution(* com.vectorx.spring5.s17_transaction_xml.service.TransferRecordService.*(..) )"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="p"></aop:advisor>
</aop:config>
Copy the code

The overall content of the Spring configuration file


      
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/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">

    <! - configure the dataSource - >
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${mysql.driverClassName}"/>
        <property name="url" value="${mysql.url}"/>
        <property name="username" value="${mysql.username}"/>
        <property name="password" value="${mysql.password}"/>
    </bean>

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

    <! -- Configure transaction manager -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <! Configure Dao creation and property injection -->
    <bean id="transferRecordDao" class="com.vectorx.spring5.s17_transaction_xml.dao.TransferRecordDaoImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"></property>
    </bean>

    <! Service creation and property injection -->
    <bean id="transferRecordService" class="com.vectorx.spring5.s17_transaction_xml.service.TransferRecordService">
        <property name="transferRecordDao" ref="transferRecordDao"></property>
    </bean>

    <! Configure transaction notification -->
    <tx:advice id="txAdvice">
        <tx:attributes>
            <tx:method name="transferAccounts" propagation="REQUIRED" isolation="REPEATABLE_READ" read-only="false"
                       timeout="5" rollback-for="java.lang.ArithmeticException"/>
        </tx:attributes>
    </tx:advice>

    <! Configure pointcuts and facets -->
    <aop:config>
        <aop:pointcut id="p"
                      expression="execution(* com.vectorx.spring5.s17_transaction_xml.service.TransferRecordService.*(..) )"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="p"></aop:advisor>
    </aop:config>
</beans>
Copy the code

Remove annotations to the Service and Dao classes and make minor code changes

public class TransferRecordDaoImpl implements TransferRecordDao {
    private JdbcTemplate jdbcTemplate;

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
    / /...
}
public class TransferRecordService {
    private TransferRecordDao transferRecordDao;

    public void setTransferRecordDao(TransferRecordDao transferRecordDao) {
        this.transferRecordDao = transferRecordDao;
    }
    / /...
}
Copy the code

Run the test code

The background results

Exception in thread "main" java.lang.ArithmeticException: / by zero
Copy the code

Database results

3.9. Complete annotation development

// Indicates that this class is a configuration class
@Configuration
// Automatically scan packets
@ComponentScan(basePackages = "com.vectorx.spring5.s18_transaction_annotation")
// Start the transaction
@EnableTransactionManagement
// Read the external configuration file
@PropertySource(value = {"classpath:jdbc.properties"})
public class TxConfig {
    @Value(value = "${mysql.driverClassName}")
    private String driverClassName;
    @Value(value = "${mysql.url}")
    private String url;
    @Value(value = "${mysql.username}")
    private String username;
    @Value(value = "${mysql.password}")
    private String password;

    / / configure the dataSource
    @Bean
    public DruidDataSource getDruidDataSource(a) {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driverClassName);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }

    / / configured JdbcTemplate
    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
        // The IOC container finds the DataSource based on the type
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

    // Configure the transaction manager
    @Bean
    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        returntransactionManager; }}Copy the code

Here we explain each annotation one by one

  • @Configuration: indicates that this class is a configuration class, which is equivalent to creating onebean.xml, i.e.,

      
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
Copy the code
  • @ComponentScan: Automatic packet scanning,basePackagesProperty to the packet path to scan, equivalent to<context:component-scan>Label, that is,
<! -- Enable annotation scan -->
<context:component-scan base-package="com.vectorx.spring5.s18_transaction_annotation"/>
Copy the code
  • @EnableTransactionManagement: Enables transaction management, equivalent to<tx:annotation-driven>Label, that is,
<! -- Enable transaction annotations -->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
Copy the code
  • @PropertySource: Import external files,valueConfigure the external file path, equivalent to<context:property-placeholder>The label
<context:property-placeholder location="classpath:jdbc.properties"/>
Copy the code
  • @Value: Injects an attribute of a common type, and its value is availableThe ${}Expression to obtain configuration information from external files
  • @Bean: Configuration object creation, equivalent to<bean>The label. A type managed by the IOC container can be passed in the decorated method parameter list, and the IOC container will automatically find the corresponding object based on the type and inject it into the method. So whether you configure the JdbcTemplate or the transaction manager, you can reference external beans in this way
/ / configured JdbcTemplate
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource) {... }// Configure the transaction manager
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) {... }Copy the code

The test code

It is important to note that the previously created objects are ClassPathXmlApplicationContext, and now is completely annotation development, so the need to create the object is AnnotationConfigApplicationContext, Just pass in the class type of the configuration class in the construct parameter. The rest of the code remains the same

ApplicationContext context = new AnnotationConfigApplicationContext(TxConfig.class);
TransferRecordService transferRecordService = context.getBean("transferRecordService", TransferRecordService.class);
transferRecordService.transferAccounts(100."Lucy"."Mary");
Copy the code

The test results

summary

The key to master

  • Spring transactions are managed in two ways:Programmatic transaction management(Not recommended),Declarative transaction management(recommended)
  • Spring transaction Management API:PlatformTransactionManager,DataSourceTrasactionManager,HibernateTransactionManager
  • Declarative transactions can be implemented in two ways:Annotation wayandXML way
  • Transaction parameters are:Propagation behavior, isolation level, timeout, read-only, (not) rollback
    • Propagation behavior: There are 7 propagation properties,REQUIRED,REQUIRED_NEW,SUPPORTS,NOT_SOPPORTED,MANDATORY,NEVER,NESTED
    • Isolation level: There are three typical “read” problems,Dirty read, unrepeatable read, virtual (phantom) read, 4 isolation levels can be set,READ UNCOMMITTED,READ COMMITTED,REPEATABLE READ,SERIALIZABLE
    • Other parameters:timeout,readOnly,rollbackFor,noRollbackFor
  • Declarative transactions (annotations) :@Transactional
  • Declarative transactions (XML) : configure the transaction manager; Configuring transaction Notification<tx:advice>; Configure pointcuts and facets
  • Fully annotated development:@EnableTransactionManagement,@Bean,AnnotationConfigApplicationContext

conclusion

  1. JdbcTemplatetheCRUDoperation
  2. The transactionACIDFeatures, Spring transaction management
  3. Annotations and XML for declarative transactions
  4. Transaction-related properties: propagation behavior, isolation level, other parameters

The following mind map is for your reference