Spring Framework Tutorial (6): Transaction Management

1. Concept

  A transaction is the most basic unit of database operations. It is a logical set of operations. Either all succeeds. If one operation fails, all operations will not be executed.

  Features:

  • Atomicity: This set of operations must be executed together or none at all.
  • Consistency: To ensure the consistency of the data, for example, 100 yuan will be added to the receiver if it is transferred out.
  • Isolation: transactions cannot affect each other.
  • Persistence: Once the execution is successful, the transaction changes to the database are irreversible.

2. Build a business operating environment

1. Three-tier structure

  • web layer: for users
  • service layer: business operations
  • Dao layer: database access and operation

  Let's take bank transfer as an example: First, the dao layer needs two methods-transfer-out method and transfer-in method. Then, the method that needs to be created in the service layer is the transfer method, which is implemented by calling the two methods of dao.

2. Specific steps

  • Create the table person in the database spring_db as follows:

Insert picture description here

  • We manually add two records, as follows:

Insert picture description here

  • Create the Person entity class under the entity package:
	package com.wang.entity;
	
	public class Person {
    
    
	    private String person_id;
	    private String person_name;
	    private int person_money;
	
	    public Person(String person_id, String person_name, int person_money) {
    
    
	        this.person_id = person_id;
	        this.person_name = person_name;
	        this.person_money = person_money;
	    }
	
	    public String getPerson_id() {
    
    
	        return person_id;
	    }
	
	    public Person() {
    
    
	    }
	
	    public void setPerson_id(String person_id) {
    
    
	        this.person_id = person_id;
	    }
	
	    public String getPerson_name() {
    
    
	        return person_name;
	    }
	
	    public void setPerson_name(String person_name) {
    
    
	        this.person_name = person_name;
	    }
	
	    public int getPerson_money() {
    
    
	        return person_money;
	    }
	
	    public void setPerson_money(int person_money) {
    
    
	        this.person_money = person_money;
	    }
	}
  • Create the PersonService class, PersonDao interface, and PersonDaoImpl implementation class under the service package and dao package, and add corresponding annotations and specific methods:
@Service
public class PersonService {
    
    
    @Autowired
    private PersonDao personDao;

    //转账操作
    public boolean transfer(Person p1,Person p2,int money){
    
    
        //先检查p1钱够不够
        if(personDao.queryMoneyById(p1)<money)return false;
        //钱够,转出,转入
        else{
    
    
            personDao.updateMoneyById(p1,money);
            personDao.updateMoneyById(p2,money*(-1));
        }
        return true;
    }
}

public interface PersonDao {
    
    

    void updateMoneyById(Person p2, int i);

    int queryMoneyById(Person p1);
}

@Component
public class PersonDaoImpl implements PersonDao {
    
    
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void updateMoneyById(Person p2, int i) {
    
    
        String sql="update person set person_money=person_money+? where person_id=?";
        Object[] arg={
    
    i,p2.getPerson_id()};
        jdbcTemplate.update(sql,arg);
        System.out.println("转账成功");
    }

    @Override
    public int queryMoneyById(Person p1) {
    
    
        String sql="select ifnull(person_money,0) from person where person_id=?";
        int money=jdbcTemplate.queryForObject(sql,Integer.class,p1.getPerson_id());
        System.out.println("得到id为"+p1.getPerson_id()+"的用户的余额为:"+money);
        return money;
    }
}
  • have a test:
	@Test
    public void testPerson(){
    
    
        ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml");
        PersonService personService=context.getBean("personService",PersonService.class);
        //转账
        Person p1=new Person();Person p2=new Person();
        p1.setPerson_id("1");p2.setPerson_id("2");
        int money=50;
        personService.transfer(p1,p2,money);
    }

Insert picture description here

Insert picture description here

3. Introduction of transaction scenarios

  Let's take a look at the transfer method in the PersonService class:

 	//转账操作
    public boolean transfer(Person p1,Person p2,int money){
    
    
        //先检查p1钱够不够
        if(personDao.queryMoneyById(p1)<money)return false;
        //钱够,转出,转入
        else{
    
    
            personDao.updateMoneyById(p1,money);
            personDao.updateMoneyById(p2,money*(-1));
        }
        return true;
    }

  Imagine that if an abnormality occurs in the system during the transfer-out operation, a situation that may be caused is that the account of p1 has less money, but the account of p2 does not increase the money. This is the problem: the abnormality leads to data Inconsistent.

  In order to solve this problem, we need to introduce transaction operations (programming implementation), and its general structure is:

try{
    
    
	//1开启事务

	//2业务操作

	//3若无异常,提交事务
}
catch(Exception e){
    
    
	//4出现异常,事务回滚
}

Three, business management introduction

  • It is most appropriate to add transaction management to the service layer (business logic layer).
  • There are two ways to implement transaction management: programmatic (the last one we gave in the previous section is programmatic, which is very inconvenient) and declarative (recommended).
  • There are two types of declarative transaction management: annotation-based (recommended) and configuration file.
  • Declarative transaction management, the underlying principle is AOP.
  • The related interface PlatformTransactionManager has different implementation classes for different frameworks.

Four, declarative transaction management

1. Annotation implementation

  • Configure the transaction manager in the configuration file:
	<!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
  • Introducing the tx namespace: the process is omitted

  • Turn on transaction annotations:

	<!--开启事务注解-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
  • Add transaction annotation @Transactional to the service class or method in the service class. If it is added to a class, it means that all methods in the class will become a transaction; if it is added to a method, it means that only this method becomes a transaction.

2. Parameter configuration for annotations

  The @Transactional annotation has a total of 6 parameters:

  • propagation: transaction propagation behavior
  • isolation: transaction isolation level
  • timeout: timeout period
  • readOnly: Whether read-only
  • rollbackFor: rollback
  • noRollbackFor: do not roll back

(1)propagation: transaction propagation behavior

  Calling between multiple transaction methods, how transactions are managed in this process. Suppose we have two methods add and update. The add method calls update internally.

  • Level 1 REQUIRED, add itself has a transaction, after calling update, the transaction in add is also used; if add itself has no transaction, a new transaction will be created in add after calling update.
  • Level 2 REQUIRED_NEW, regardless of whether there is a transaction in add itself, a new transaction is created in add after update is called.

Insert picture description here

  The remaining several levels are not required to be remembered, but the first two need to be remembered.

(2) isolation: transaction isolation level

  There is isolation between transactions, and there will be no impact between multi-transaction operations. The isolation of transactions must be considered. If you do not consider it, problems will occur: dirty reads, non-repeatable reads, and virtual reads.

  • Dirty read: An uncommitted transaction reads data from another uncommitted transaction.

Insert picture description here

  • Non-repeatable read: Two identical queries within the scope of a transaction return different data. .

Insert picture description here

  • Phantom read/phantom read: The same two reads in a transaction, the second time reads the newly inserted row of another transaction.

  • Transaction isolation level: is the solution to the above problems

Insert picture description here

(3)timeout: timeout

  The transaction needs to be committed within a certain period of time, and it will be rolled back if it is not committed. The default value is -1, which means no timeout, in seconds.

(4)readOnly: Whether read-only

  The default value is false, which means not only can read but also write, and it can be set to true, which means that only query operations can be done.

(5)rollbackFor: rollback

  Set which exceptions occur for transaction rollback.

(6) noRollbackFor: no rollback

  Set which exceptions occur without transaction rollback.

3.xml implementation

  • Configure transaction manager: tx namespace, connection pool, JdbcTemplate, transaction manager
  • Configure notifications: enhancements
	<!--配置通知-->
    <tx:advice id="txadvice">
        <!--配置事务参数-->
        <tx:attributes>
            <!--配置哪些方法,以及相应的事务属性-->
            <!--针对名为acountMoney-->
            <tx:method name="acountMoney" propagation="REQUIRED" isolation="DEFAULT"/>
            <!--针对所有以acount开头的方法名-->
            <tx:method name="account*"/>
        </tx:attributes>
    </tx:advice>
  • Configure entry points and aspects
	<!--配置切入点和切面-->
    <aop:config>
        <!--配置切入点-->
        <aop:pointcut id="point" expression="execution(* com.wang.service.UserService.*(..))"/>
        <!--配置切面-->
        <aop:advisor advice-ref="txadvice" pointcut-ref="point"/>
    </aop:config>

4. Fully annotated development (how to write configuration class)

@Configuration
@ComponentScan(basePackages="com.wang")//组件扫描
@EnableTransactionManagement//开启事务
public class txConfig {
    
    
    //创建数据库的连接池
    @Bean
    public DruidDataSource getDruidDataSource(){
    
    
        DruidDataSource dataSource=new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql:///spring_db");
        dataSource.setUsername("root");
        dataSource.setPassword("wang1996526");
        return dataSource;
    }

    //创建JdbcTemplate对象
    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource){
    
    
        JdbcTemplate jdbcTemplate=new JdbcTemplate();
        //注入dataSource
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

    //创建事务管理器
    @Bean
    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
    
    
        DataSourceTransactionManager transactionManager=new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}

Guess you like

Origin blog.csdn.net/Tracycoder/article/details/112916554