Spring - Examples of transactions and advantages and disadvantages of using the @Transactional annotation and AspectJ framework

Posted by BinaryDragon on Tue, 28 Dec 2021 02:35:49 +0100

1. Reflect transactions through examples

1.1 Build two tables from a database

The sale table stores the sales records, the id represents the number of the sales records, and the primary key is the automatic growth. gid is the number of goods purchased; num is the quantity of goods purchased. Initially, there is no data in the sale table. (

 

The goods table stores specific information about each item. id is the commodity number, the primary key; Name is the name of the commodity; amount is a stock of goods; Price is the unit price of a commodity.

 

 

 

1.2 Maven dependencies (pom.xml) needed to join a project

 <dependencies>
<!--Test class dependency-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <!--  Spring rely on-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <!--    spring Transaction Dependency-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <!--    mybatis rely on-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.1</version>
    </dependency>
    <!--    spring and mybatis Integrate-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.1</version>
    </dependency>
    <!--    mysql drive-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.9</version>
    </dependency>
    <!--    Connection Pool of Ali-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.12</version>
    </dependency>

  </dependencies>

  <build>
    <resources>
      <resource>
        <directory>src/main/java</directory><!--In the directory-->
        <includes><!--Include under directory.properties,.xml All files will be scanned to-->
          <include>**/*.properties</include>
          <include>**/*.xml</include>
        </includes>
        <filtering>false</filtering>
      </resource>
    </resources>
  </build>

1.3 Write entity classes (Goods class, Sale class)

package com.liuhaiyang.entity;

public class Goods {
   //Commodity number, primary key
    private Integer id;
    //Commodity Name
    private String name;
    //Commodity Inventory
    private  Integer amount;
    //item pricing
    private Float price;

    //set and get, tostring, parametric construction, parametric construction
}
package com.liuhaiyang.entity;

public class Sale {
   //Primary key
    private Integer id;
    //id of the item purchased
    private Integer gid;
    //Quantity of goods purchased
    private Integer num;

    //set and get, tostring, parametric construction, parametric construction
}

1.4 Write dao interfaces and corresponding mapper mapping files

1.4. 1 dao interface and mapper file corresponding to commodity entity class Goods

package com.liuhaiyang.dao;

import com.liuhaiyang.entity.Goods;

public interface GoodsDao {
    //Query commodities based on commodity id
    Goods selectById(Integer id);

    //The goods parameter indicates the id of the item purchased and the quantity of the item purchased
    //ID commodity id:amount: quantity of this item purchased this time
    int updateGoods(Goods goods);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.liuhaiyang.dao.GoodsDao">
    <!--Use insert,uodate,delete,select Label Writing sql-->
    <select id="selectById" resultType="com.liuhaiyang.entity.Goods">
        select  * from goods where  id=#{id}
    </select>

    <update id="updateGoods">
        update  goods set amount=amount-#{amount} where id=#{id}
    </update>
</mapper>

1.4. 2. Sales record entity class Sale dao interface and mapper file

package com.liuhaiyang.dao;

import com.liuhaiyang.entity.Sale;

public interface SaleDao {
    int intsertsale(Sale sale);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.liuhaiyang.dao.SaleDao">
    <!--Use insert,uodate,delete,select Label Writing sql-->

    <insert id="intsertsale" >
        insert into sale(gid,nums) values (#{gid},#{nums})
    </insert>
</mapper>

1.5 Write MyBatis Main Profile

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--    Set Log-->
        <settings>
            <setting name="logImpl" value="STDOUT_LOGGING"/>
        </settings>


<!--    alias-->

<!--    <typeAliases>-->
<!--        <package name="com.liuhaiyang.entity"/>-->
<!--    </typeAliases>-->
    <!--Specify Other mapper File location to find other files sql Sentence-->
    <mappers>
<!--        <mapper resource="com/lhy/dao/studentDao.xml"/>-->
        <package name="com.liuhaiyang.dao"/>
    </mappers>
</configuration>

1.6 Define exception classes (runtime exceptions)

This exception class is mainly used to handle the exceptional information that occurs when purchasing a commodity, whether or not there is an inventory or a commodity.

package com.liuhaiyang.excetion;
//Runtime Exceptions
public class NotException extends RuntimeException{
    public NotException() {
        super();
    }

    public NotException(String message) {
        super(message);
    }
}


1.7 Define Service interfaces and corresponding implementation classes

package com.liuhaiyang.service;

public interface BuyGoodsService {
    // Goods purchased ID means goods purchased ID num means the quantity of goods purchased
    void buyboods(Integer goodsid,Integer num);
}

1.7.1 @Transactional control transaction

All of the optional properties for @Transactional are as follows:

1. propagation: Used to set transaction propagation properties. The property type is the Propagation enumeration, and the default value is Propagation.REQUIRED.

2. isolation: Used to set the isolation level of a transaction. The attribute type is the Isolation enumeration and the default value is Isolation.DEFAULT.

3. readOnly: Sets whether the method is read-only for database operations. The property is boolean and the default value is false.

4. timeout: Sets the timeout period for this operation to connect to the database. The unit is seconds, the type is int, and the default value is -1, which means there is no time limit.

5. rollbackFor: Specifies the exception class that needs to be rolled back. The type is Class[], and the default is an empty array. Of course, if there is only one exception class, you can not use arrays.

6. rollbackForClassName: Specifies the exception class name that needs to be rolled back. The type is String[], and the default is an empty array. Of course, if there is only one exception class, you can not use arrays.

7. noRollbackFor: Specifies an exception class that does not require rollback. The type is Class[], and the default is an empty array. Of course, if there is only one exception class, you can not use arrays.

8.noRollbackForClassName: Specifies the exception class name that does not require rollback. The type is String[], and the default is an empty array. Of course, if there is only one exception class, you can not use arrays. (

It is important to note that if @Transactionalis used on a method, it can only be used on a public method. For other non-public methods, with the comment @Transactional, Spring does not error, but does not weave the specified transaction into the method. Because Spring ignores the @Transaction annotation on all non-public methods. If the @Transaction annotation is on a class, it means that all methods on that class will be woven into transactions at execution time.

package com.liuhaiyang.service.impl;

import com.liuhaiyang.dao.GoodsDao;
import com.liuhaiyang.dao.SaleDao;
import com.liuhaiyang.entity.Goods;
import com.liuhaiyang.entity.Sale;
import com.liuhaiyang.excetion.NotException;
import com.liuhaiyang.service.BuyGoodsService;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;


public class BuyGoodsServiceImpl implements BuyGoodsService {
    private GoodsDao goodsDao=null;
    private SaleDao saleDao=null;

    public void setGoodsDao(GoodsDao goodsDao) {
        this.goodsDao = goodsDao;
    }

    public void setSaleDao(SaleDao saleDao) {
        this.saleDao = saleDao;
    }

//    Transaction Notes

   /*@Transactional Put above the public method to indicate that the method has transactional functionality. Note that it cannot be added when it is private, but only when it is public
    * Features: 1) Transactions provided by the spring framework itself
    *       2)Suitable for small and medium-sized projects - change in code
    *       3)Easy to use and high efficiency
    */
   /* First way
   @Transactional(
            propagation = Propagation.REQUIRED,  // Runtime exception rollback for specified propagation behavior value
            isolation =  Isolation.DEFAULT,  //Represents isolation level
            readOnly = false,timeout = 20,  // readOnly,Read-only or not, default is false timeout for timeout
            rollbackFor = { NullPointerException.class,NotException.class}  //Rollback is required when an exception occurs
    )*/

    /* Second way:
    @Transactional(
            propagation = Propagation.REQUIRED,  //Indicates that there is always a transaction
            isolation =  Isolation.DEFAULT,  //Represents isolation level
            readOnly = false,timeout = 20)
    rollbackFor Use:
    1)The framework first checks if the exception thrown by the method is in the rollbackFor s array, if it must be rolled back.
    2)If the exception thrown by the method is not in the rollbackFor array, the framework continues to check if the exception thrown is in a RuntimeException.
        If it is a RuntimeException, it must be rolled back

        In special cases, the first is used, for example, when a SqlException IOException is thrown, a non-runtime exception can be used as the first
        rollbackFor={SQLException.class,IOException.class}
     */
    //Third, @Transactional means that all are defaults, and REQUIRED means that rollback to default when an exception occurs
    @Transactional
    @Override
    public void buyboods(Integer goodsid, Integer num) {
        System.out.println("buy How to start!!!");

        //Sales record
        Sale sale=new Sale();
        sale.setQid(goodsid);
        sale.setNums(num);
        saleDao.intsertsale(sale);
        //Query commodities
        Goods goods1=goodsDao.selectById(goodsid);
        if (goods1== null){
            throw new NullPointerException(goods1+"Commodity does not exist");
        } else if (goods1.getAmount()<num){
            throw new NotException(goods1+"Insufficient quantity of goods");
        }else {
            System.out.println(goods1);
        }


        //Update Inventory
        Goods goods=new Goods();
        goods.setId(goodsid);
        goods.setAmount(num);
        goodsDao.updateGoods(goods);


        System.out.println("buy Method completed!!!");
    }
}

1.8 Define Spring Profile

1.8. 1 Introduce external property profile (jdbc.properties)

jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&amp;characterEncoding=utf-8
jdbc.username=root
jdbc.password=123456

1.8. 2 Declare data source, SqlSessionFactory object, read mapper file, etc.

<?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"
       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">
<!--       Load import external property profile-->
        <context:property-placeholder location="classpath:jdbc.properties"/>
        <!--Declare Data Source DataSource   There can be multiple data sources-->
        <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>

        <!--statement SqlSessionFactoryBean,Inside this class, create SqlSessionFactory-->
        <bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
                <!--specify data source -->
                <property name="dataSource" ref="myDataSource"/>
                <!--Appoint myBatis Main Profile
                Resource Can be used directly value Attribute Assignment-->
                <property name="configLocation" value="classpath:mybatis.xml"/>
        </bean>

        <!--statement MapperScannerConfiguration
        SqlSession.getMapper(StudentDao.class)
        MapperScannerConfigurer Effect:
        loop basePackage Represented package, find each interface in the package, call SqlSession.getMapper
        Put each dao Interfaces are created dao Object, dao Proxy object methods into containers.

          Amount to:
          ApplicationContext ctx=...
          SqlSessionFactory sqlSessionFactory=ctx.getBean("factory");
          for(Interface: com.liuhaiyang.dao){
          Interface Object=session.getMapper(Interface)
          springMap.put(Object name, object)
          }
        -->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--                Appoint sqlsessionFactory Name of object-->
                <property name="sqlSessionFactoryBeanName" value="factory"/>
<!--                Specify the base package, dao Package name of the interface-->
                <property name="basePackage" value="com.liuhaiyang.dao"/>
        </bean>

<!--        statement service-->
        <bean id="buyselect" class="com.liuhaiyang.service.impl.BuyGoodsServiceImpl">
                <property name="goodsDao" ref="goodsDao"/>
                <property name="saleDao" ref="saleDao"/>
        </bean>
<!--       <context:component-scan base-package="com.liuhaiyang.service"/>-->

<!--        Declare transaction control-->
<!--        Declare Transaction Manager  id It can be any value, preferably transactionManager-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--                specify data source DataSources-->
                <property name="dataSource" ref="myDataSource"/>
        </bean>
<!--        Turn on Transaction Annotation Driver: Tell the framework that using annotations to manage transactions is typically tx Ending
                Attributes: transaction-manager Specify Transaction Manager's id There can be multiple id(You can also have multiple data sources)
-->
        <tx:annotation-driven transaction-manager="transactionManager"/>

</beans>

1.9 Define test classes

1.9. 1 Test Class 1

import com.liuhaiyang.service.BuyGoodsService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class test {
    @Test
    public void test01(){
        String config="application.xml";
        ApplicationContext app=new ClassPathXmlApplicationContext(config);
        BuyGoodsService buy=(BuyGoodsService) app.getBean("buyselect");

        buy.buyboods(1001,20);

    }
}

Result Screenshot:

 

 

 

1.9. 2 Test Class 2 (Exception)

 @Test
    public void test01(){
        String config="application.xml";
        ApplicationContext app=new ClassPathXmlApplicationContext(config);
        BuyGoodsService buy=(BuyGoodsService) app.getBean("buyselect");

        buy.buyboods(1001,50);

    }

Result Screenshot:

Database

 

 

2. Use the AspectJ framework to control transactions

The disadvantage of configuring transaction agents with XML is that each target class needs to be configured with a transaction agent. When there are more target classes, the configuration file becomes very bloated.

Use XML Configuration Advisor to automatically generate a transaction proxy for each class that matches the entry point expression. The usage is simple, simply delete the configuration of the transaction proxy in the previous code and replace it with the following.

The steps to control transactions in this way are as follows:

In pom. Add spring-aspects dependency to XML
Declare the contents of a transaction in the spring configuration file: 1 Declare the transaction manager; (2) Declare transaction attributes required by business methods; 3. Declare the entry point expression.
Most of the code is the same as using the comment (@Transactional) above. Only Spring's configuration file has changed.

Delete the declare transaction annotation driver in the spring configuration file in the first method and replace it with the code below.

<!--        2.Declare transaction attributes of the business party (isolation level, propagation behavior, timeout)
            id:Give the business method configuration transaction segment code a name, unique value (customizable)
            transaction-manager: Transaction Manager id
-->
        <tx:advice id="selectAdvice" transaction-manager="transactionManager">
                <tx:attributes>
<!--                    Give a specific business method the transaction properties he needs
                        name: Name of business method. To configure name Value of: 1.Name of business method: 2.With wildcards(*)Method Name 3.Use*
                        propagation: Value specifying propagation behavior isolation: Isolation Level read-only: Read-only or not. Default is false  timeout Represents a timeout
                        rollback-for: Specify a list of rollback exception types, using the exception fully qualified name-->
                        <tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT" read-only="false"
                                   timeout="20" rollback-for="com.liuhaiyang.excetion.NotException,java.lang.NullPointerException"/>

<!--                        Represents all of the buy Beginning business method for transactional operations-->
<!--                       <tx:method name="buy*" Wait/>-->

<!--                        Individual uses other than the above represent all, and other uses represent methods other than-->
<!--                        <tx:method name="*" Wait/>-->
                </tx:attributes>
        </tx:advice>


<!--        Declare entry point expressions: represent classes in those packages, method parameters and transactions in classes-->
        <aop:config>
<!--            Declare entry point expression
                expression: Breakthrough point expressions to express methods in those classes and classes
                id: Represents the name of the entry point expression, unique value(Any value)-->
                <aop:pointcut id="servicePointcut" expression="execution(* *..service.*(..))"/>
<!--                Associate entry point expressions with transaction notifications-->
                <aop:advisor advice-ref="selectAdvice" pointcut-ref="servicePointcut"/>
        </aop:config>

3 @Transactional annotations and AspectJ framework features

1. Use the @Transactional annotation

1. Transaction control provided by the Spring framework itself.
2. Suitable for small and medium-sized projects.
3. Easy to use and high efficiency.

2. Using the AspectJ framework

Disadvantages: Difficult to understand and complex to configure.
Advantage: Code and transaction configurations are separate. Controls the transaction without modifying its source code. Ability to quickly understand and control the entire transaction of a project, suitable for large projects.

Topics: Java Spring