Antecedents to review

You have learned the basics of Spring and Spring AOP, and in the previous section we used Spring and MyBatis together to familiarize ourselves with the development pattern. This section is also important for learning about transactions in Spring.

The transaction concept

In fact, it is the same concept as the transaction that we learned before MySql. It refers to the execution results of one or more SQL statements, which are either all successful or all failed. There is no other result. The emergence of transactions is also to solve problems in real life.

Spring transaction manager

Use Spring’s transaction manager to manage transactions for different database access technologies. Developers only need to master one solution, Spring transaction processing, to achieve transaction management without database access technology.

Transaction management is oriented towards Spring, which manages transactions, commits transactions, and rolls back transactions.

Transaction manager interface: PlatformTransactionManager, it has a lot of implementation class, basically different database access technology has a corresponding implementation class, we are going to study is DataSourceTransactionManager, The implementation class is suitable for JDBC mode and MyBatis mode.

Spring transactions define interfaces

The TransactionDefinition interface defines three types of constants that describe properties related to transaction control.

Transaction attributes:

  • Isolation level
  • Propagation behavior
  • Timeout of transactions

Spring transaction isolation levels and propagation

Isolation level: Controls the degree of impact between different transactions. This is the same as learning about transaction isolation in MySQL.

Five values, with only four isolation levels:

  1. DEFAULT: Use the DEFAULT transaction isolation level of the database and the DEFAULT transaction isolation level of MySQLREPEATABLE_READOracle defaultREAD_UNCOMMITTED
  2. READ_UNCOMMITTED: Read is not committed, and no concurrency problems are resolved.
  3. READ_COMMITTED: Reads are committed, dirty reads are resolved, unrepeatable reads and magic reads exist
  4. REPEATABLE_READ: repeatable read, solve the dirty read, non-repeatable read, phantom read
  5. SERIALIZABLE: SERIALIZABLE without concurrency problems.

Spring transaction propagation behavior, indicating whether a method has a transaction:

  • PROPAGATION_REQUIRED: Spring’s default propagates behavior, calling the current transaction if there is one, or a new transaction if there is none, and the method executes in the new transaction.
  • PROPAGATION_SUPPORTS: Supports, a method executes with or without a transaction.
  • PROPAGATION_SUPPORTS_NEW: New, if a transaction exists during the method call, it is suspended until the new transaction is executed. If no transaction exists, create a new transaction execution method.
  • PROPAGATION_MANDATORY:
  • PROPAGATION_NESTED:
  • PROPAGATION_NEVER:
  • PROPAGATION_NOT_SUPPORTED:

The first three must be mastered.

Spring adds transactions

Spring provides two ways to add transactions:

  • Spring framework’s own annotations@Transactional
  • useAspectJAOP configuration for the framework

Spring transaction application case

Scene demand

Sell simple process, when selling a product, so sales record should increase a record of the goods at the same time, the goods should be updated in the table this goods inventory, and the two should be successful at the same time, or fail at the same time, is the right business logic, and to ensure that the results, you need to use the transaction.

Implement process analysis

Except for business analysis and adding transactions, the process is the same as in the previous section. Let’s review the steps:

  1. Create databases and tables

    We need to create two new tables: Sale and goods.

    Id (automatic growth, int, primary key, sales record ID); Gid (int, commodity ID); Num (int, purchase quantity);

    Id (autoincrement, int, primary key, commodity ID); Name (varchar(255), commodity name); Account (int, commodity inventory); Price (float, unit price of an item);

    Among them, two data can be maintained in the goods table first. The data here are as follows:

    1001,50, mobile phone 5G, 5299 1002,10, display 4K, 2299Copy the code
  2. Creating an entity Class

    This operation is estimated that you are already familiar with the road, nonsense not to say, on the code:

    Sale entity class code:

    public class Sale {
    
        private Integer id;
        private Integer gid;
        private Integer num;
    
        public Integer getId(a) {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public Integer getGid(a) {
            return gid;
        }
    
        public void setGid(Integer gid) {
            this.gid = gid;
        }
    
        public Integer getNum(a) {
            return num;
        }
    
        public void setNum(Integer num) {
            this.num = num; }}Copy the code

    Goods entity class code:

    public class Goods {
    
        private Integer id;
        private String name;
        private Float price;
        private Integer account;
    
        public Integer getId(a) {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName(a) {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Float getPrice(a) {
            return price;
        }
    
        public void setPrice(Float price) {
            this.price = price;
        }
    
        public Integer getAccount(a) {
            return account;
        }
    
        public void setAccount(Integer account) {
            this.account = account; }}Copy the code
  3. Create Dao and corresponding Mapper

    The SaleDao code is as follows:

    public interface SaleDao {
    
        int insertSale(Sale sale);
    }
    Copy the code

    Here is a new sales record, so all you need is an interface to add records.

    SaleDaoMapper. XML is as follows:

    
            
    <! DOCTYPEmapper
            PUBLIC "- / / mybatis.org//DTD Mapper / 3.0 / EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
    <mapper namespace="com.javafirst.dao.SaleDao">
    
        <insert id="insertSale">
            insert into sale(gid,num) values (#{gid},#{num})
        </insert>
    
    </mapper>
    Copy the code

    The GoodsDao code is as follows:

    public interface GoodsDao {
    
        int updateAccount(Goods goods);
    
        Goods selectById(Integer gid);
    }
    Copy the code

    Two methods are provided: one is that we need to update the inventory usage; the other is that we need to query the commodity inventory according to the given ID to avoid negative inventory after the update.

    The code for goodsdaomapper.xml is as follows:

    
            
    <! DOCTYPEmapper
            PUBLIC "- / / mybatis.org//DTD Mapper / 3.0 / EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
    <mapper namespace="com.javafirst.dao.GoodsDao">
    
        <update id="updateAccount">
            update goods set account = account - #{account} where id = #{id}
        </update>
    
        <select id="selectById" resultType="com.javafirst.daomain.Goods">
            select * from goods where id = #{id}
        </select>
    </mapper>
    Copy the code
  4. MyBatis core configuration

    This is the old rule, on the code:

    The code for mybatis-config.xml is as follows:

    
            
    <! DOCTYPEconfiguration
            PUBLIC "- / / mybatis.org//DTD Config / 3.0 / EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
    
        <! - log - >
        <settings>
            <setting name="logImpl" value="STDOUT_LOGGING"/>
        </settings>
    
        <typeAliases>
            <typeAlias type="com.javafirst.daomain.Sale"/>
            <typeAlias type="com.javafirst.daomain.Goods"/>
        </typeAliases>
    
        <mappers>
            <mapper resource="mapper/SaleDaoMapper.xml"/>
            <mapper resource="mapper/GoodsDaoMapper.xml"/>
        </mappers>
    </configuration>
    Copy the code
  5. Create the Service and its implementation class

    In fact, this is our real business logic processing, the previous is support, this overall process needs more cases, slowly feel.

    public interface BuyGoodsService {
    
        void buyGoods(Integer gid, Integer num);
    }
    Copy the code

    Its implementation class code is as follows:

    /** * desc: * author: recommend learning Java * 

    * weChat: studyingJava */

    public class BuyGoodsImpl implements BuyGoodsService { SaleDao saleDao; GoodsDao goodsDao; public void setSaleDao(SaleDao saleDao) { this.saleDao = saleDao; } public void setGoodsDao(GoodsDao goodsDao) { this.goodsDao = goodsDao; } @Override public void buyGoods(Integer gid, Integer num) { System.out.println("The buyGoods method has been implemented..."); // Insert records first and then subtract inventory Sale sale = new Sale(); sale.setGid(gid); sale.setNum(num); saleDao.insertSale(sale); // Determine whether the inventory is greater than or equal to the purchased quantity before reducing the inventory to avoid negative inventory after updating Goods goods = goodsDao.selectById(gid); if (null == goods || gid > 1002 || gid < 1001) { throw new NullPointerException("Goods don't exist."); } if (goods.getAccount() < num) { throw new IndexOutOfBoundsException("Out of stock"); } // Update inventory Goods buyGoods = newGoods(); buyGoods.setId(gid); buyGoods.setAccount(num); goodsDao.updateAccount(buyGoods); }}Copy the code

    The logic here is to run through the entire process, not before the transaction logic.

  6. Spring core configuration file

    Applicationcontext.xml reads as follows:

    
            
    <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 https://www.springframework.org/schema/context/spring-context.xsd">
    
        <! Use properties file to configure database link information in data source.
        <context:property-placeholder location="jdbc.properties"/>
    
        <! Declare data source -->
        <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>
    
        <! Declare SQLSessionFactoryBean -->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="myDataSource"/>
            <property name="configLocation" value="classpath:mybatis-config.xml"/>
        </bean>
    
        <! -- Statement MapperScannerConfigurer -->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
    
            <property name="basePackage" value="com.javafirst.dao"/>
        </bean>
    
        <! -- Register custom Service -->
        <bean id="buyGoods" class="com.javafirst.service.impl.BuyGoodsImpl">
            <property name="saleDao" ref="saleDao"/>
            <property name="goodsDao" ref="goodsDao"/>
        </bean>
    </beans>
    Copy the code

    As mentioned in the previous section, this is mostly procedural, and we just need to register our own business related services.

  7. test

    This is all fixed code, paste:

    @Test
    public void spring_transaction(a) {
        String config = "applicationContext.xml";
        ApplicationContext context = new ClassPathXmlApplicationContext(config);
    
        BuyGoodsService buyGoods = (BuyGoodsService) context.getBean("buyGoods");
        buyGoods.buyGoods(1001.10);
    
        System.out.println("Test method execution completed!");
    }
    Copy the code

    The solution is to use Spring transactions to control the end result of the overall process. Let’s learn how to use transactions.

Add transactions using the Transactional annotation

Use steps:

  1. Declare the contents of a transaction in a Spring configuration file (declare a transaction manager, specifying which transaction manager object to use; Declare which annotation to use to manage transactions, turn on transaction annotation driver)
  2. Add to the class source code@TransactionalAnnotations (can be added to classes or methods, the latter is recommended)

Add the following code to the Spring configuration file:

<! -- Declare transaction manager -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <! Give the transaction manager the name of the data source to manage.
    <property name="dataSource" ref="myDataSource"/>
</bean>

<! -- Declare transaction manager driver -->
<tx:annotation-driven transaction-manager="transactionManager"/>
Copy the code

Then add a comment above the business code method:

Transactional(Propagation = Propagation.REQUIRED, // REQUIRED DEFAULT value isolation = Isolation.default, ReadOnly = false; // Return an array of exceptions declared in this array, and the program will roll back any exceptions declared in this array. Is not in the array declared a runtime exception will rollback rollbackFor = {NullPointerException. Class, IndexOutOfBoundsException class})
@Override
public void buyGoods(Integer gid, Integer num) {
    System.out.println("The buyGoods method has been implemented...");

    // Insert records first and then subtract inventory
    Sale sale = new Sale();
    sale.setGid(gid);
    sale.setNum(num);

    saleDao.insertSale(sale);

    // Determine whether the inventory is greater than or equal to the purchased quantity before reducing the inventory to avoid negative inventory after updating
    Goods goods = goodsDao.selectById(gid);

    if (null == goods) {
        throw new NullPointerException("Goods don't exist.");
    }
    if (goods.getAccount() < num) {
        throw new IndexOutOfBoundsException("Out of stock");
    }

    // Update inventory
    Goods buyGoods = new Goods();
    buyGoods.setId(gid);
    buyGoods.setAccount(num);

    goodsDao.updateAccount(buyGoods);
}
Copy the code

We then tested again by modifying the parameters to prove that the addition of transactions ensures that we do not insert more sales records in the event of an error operation, ensuring that the SQL logic in the business method will either all succeed or all fail.

Declare transaction control using the AspectJ framework

This is another way to use Spring transactions.

AOP that uses AspectJ to declare transaction control is called declarative transactions. The steps are as follows:

  1. inpom.xmladdspring-aspectsRely on;
  2. Declare the contents of a transaction in a Spring configuration file (declare the transaction manager, transaction attributes required by the business method, declare pointcut expressions)

The following is the way to do the test transaction use, before this, we first add transaction way comment out, in the following configuration.

Add the following code to the pom.xml file:

<! AspectJ AOP implementation annotations -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.3.14</version>
</dependency>
Copy the code

The applicationContext.xml file adds the following code:

<! -- Declare transaction manager -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <! Give the transaction manager the name of the data source to manage.
    <property name="dataSource" ref="myDataSource"/>
</bean>

<! Implementing Spring transaction configuration management using AspectJ framework AOP
<tx:advice id="buyServiceAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="buyGoods" propagation="REQUIRED" isolation="DEFAULT"
                   rollback-for="java.lang.NullPointerException,java.lang.IndexOutOfBoundsException"/>

        <! Add (*); / / add (*); / / add (*);
    </tx:attributes>
</tx:advice>

<! Pointcut expression that specifies which class in which package the transaction manager needs to be used -->
<aop:config>
    <! -- id: pointcut ID, unique value expression: pointcut expression that specifies the method to execute -->
    <aop:pointcut id="servicePointcut" expression="execution(* *.. service.. *. * (..) )"/>
    <! Associate pointcut expressions with declared transaction managers
    <aop:advisor advice-ref="buyServiceAdvice" pointcut-ref="servicePointcut"/>
</aop:config>
Copy the code

Declare that the transaction manager is fixed, and everything else is actually fixed code that can be used as a template later.

Finally, there is the test, which also verifies the correctness of the results by changing the parameters.

Two ways to compare

The former is suitable for small and medium-sized projects, the process is relatively clear, easy to understand; The latter configuration is more difficult to understand, not easy to read, suitable for large projects, generally configured will not move.

conclusion

  • The architecture and usage of Spring transaction manager introduced in this article are still new, focusing on the application scenarios of transactions, because they are needed in actual development
  • The code block must know how to use the transaction, configure itself, and make the transaction work
  • With the previous section in mind, you now need to learn how to build a Spring project from scratch, including the Dao layer and the business layer and simple configuration

“Recommended learning Java” series dry here, xiaobian seems to have a suddenly enlightened feeling, and did not begin to learn the fear, but open relaxed, we refuel ~

Learn programming, recommend the preferred Java language, xiaobian created a focus on Java original public number to recommend learning Java, you can search javaFirst attention in wechat, together to open the Java journey!