Spring integrates MyBatis notes

Required reading: learning suggestions

This tutorial is hand-in-hand teaching and introduces how to integrate MyBatis through the Spring framework. Follow the tutorial steps to complete the integration of MyBatis and Spring framework. Try it quickly! The configuration process must be careful~~

Knowledge expansion: comparison of various database connection pools: Comparison of c3p0, DBCP and Druid connection pools_ Blog of wawa3338 - CSDN blog_ dbcp druid

1, Spring integration MyBatis steps

The first step is to introduce dependent packages

The jar packages that the project depends on are as follows:

Maven: aopalliance:aopalliance:1.0

Maven: com.google.protobuf:protobuf-java:3.6.1

Maven: commons-dbcp:commons-dbcp:1.4

Maven: commons-pool:commons-pool:1.6

Maven: junit:junit:4.13

Maven: log4j:log4j:1.2.17

Maven: mysql:mysql-connector-java:8.0.18

Maven: org.aspectj:aspectjweaver:1.9.6

Maven: org.hamcrest:hamcrest:2.2

Maven: org.hamcrest:hamcrest-core:2.2

Maven: org.mybatis:mybatis:3.5.6

Maven: org.mybatis:mybatis-spring:2.0.6

Maven: org.springframework:spring-aop:5.3.4

Maven: org.springframework:spring-beans:5.3.4

Maven: org.springframework:spring-context:5.3.4

Maven: org.springframework:spring-core:5.3.4

Maven: org.springframework:spring-expression:5.3.4

Maven: org.springframework:spring-jcl:5.3.4

Maven: org.springframework:spring-jdbc:5.3.4

Maven: org.springframework:spring-tx:5.3.4

Note: to create a project using maven, all the dependent packages required are as follows. Maven: org. MyBatis: MyBatis Spring: 2.0.6 is a dependency package integrated by MyBatis using the Spring framework.  

pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.bdqn</groupId>
  <artifactId>s3-spring-mybatis</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>s3-spring-mybatis</name>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.13</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>5.3.4</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>5.3.4</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.3.4</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>5.3.4</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-expression</artifactId>
      <version>5.3.4</version>
    </dependency>
    <dependency>
      <groupId>aopalliance</groupId>
      <artifactId>aopalliance</artifactId>
      <version>1.0</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.6</version>
    </dependency>
    <dependency>
      <groupId>org.hamcrest</groupId>
      <artifactId>hamcrest-core</artifactId>
      <version>2.2</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.6</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.18</version>
    </dependency>
    <!-- Spring integration MyBatis Required package -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>2.0.6</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.3.4</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>5.3.4</version>
    </dependency>
    <dependency>
      <groupId>commons-dbcp</groupId>
      <artifactId>commons-dbcp</artifactId>
      <version>1.4</version>
    </dependency>
    <dependency>
      <groupId>commons-pool</groupId>
      <artifactId>commons-pool</artifactId>
      <version>1.6</version>
    </dependency>
  </dependencies>

  <!-- Configure resource directory -->
  <build>
    <resources>
      <resource>
        <directory>src/main/java</directory>
        <includes>
          <include>**/*.properties</include>
          <include>**/*.xml</include>
        </includes>
      </resource>
      <resource>
        <directory>src/main/resources</directory>
        <includes>
            <include>**/*.properties</include>
            <include>**/*.xml</include>
        </includes>
       </resource>
    </resources>
  </build>
</project>

The second step is to write entity classes and three-tier patterns

Entity class SysUser.java  

package com.bdqn.pojo;

import java.util.Date;

/**
 * System user entity
 *
 * @author Miku
 * @since 2021-05-14
 */
public class SysUser {

   private Integer id; //id
   private String account; //User code
   private String realName; //User name
   private String password; //User password
   private Integer sex;  //Gender
   private Date birthday;  //date of birth
   private String phone;   //Telephone
   private String address; //address
   private Integer roleId;    //User role ID
   private Integer createdUserId;   //creator
   private Date createdTime; //Creation time
   private Integer updatedUserId;     //Updater
   private Date updatedTime;   //Update time

   private Integer age;//Age
   private String roleIdName; //User role name

   public Integer getAge() {
      /*long time = System.currentTimeMillis()-birthday.getTime();
      Integer age = Long.valueOf(time/365/24/60/60/1000).IntegerValue();*/
      Date date = new Date();
      Integer age = date.getYear() - birthday.getYear();
      return age;
   }

   public Integer getId() {
      return id;
   }

   public void setId(Integer id) {
      this.id = id;
   }

   public String getAccount() {
      return account;
   }

   public void setAccount(String account) {
      this.account = account;
   }

   public String getRealName() {
      return realName;
   }

   public void setRealName(String realName) {
      this.realName = realName;
   }

   public String getPassword() {
      return password;
   }

   public void setPassword(String password) {
      this.password = password;
   }

   public Integer getSex() {
      return sex;
   }

   public void setSex(Integer sex) {
      this.sex = sex;
   }

   public Date getBirthday() {
      return birthday;
   }

   public void setBirthday(Date birthday) {
      this.birthday = birthday;
   }

   public String getPhone() {
      return phone;
   }

   public void setPhone(String phone) {
      this.phone = phone;
   }

   public String getAddress() {
      return address;
   }

   public void setAddress(String address) {
      this.address = address;
   }

   public Integer getRoleId() {
      return roleId;
   }

   public void setRoleId(Integer roleId) {
      this.roleId = roleId;
   }

   public Integer getCreatedUserId() {
      return createdUserId;
   }

   public void setCreatedUserId(Integer createdUserId) {
      this.createdUserId = createdUserId;
   }

   public Date getCreatedTime() {
      return createdTime;
   }

   public void setCreatedTime(Date createdTime) {
      this.createdTime = createdTime;
   }

   public Integer getUpdatedUserId() {
      return updatedUserId;
   }

   public void setUpdatedUserId(Integer updatedUserId) {
      this.updatedUserId = updatedUserId;
   }

   public Date getUpdatedTime() {
      return updatedTime;
   }

   public void setUpdatedTime(Date updatedTime) {
      this.updatedTime = updatedTime;
   }

   public void setAge(Integer age) {
      this.age = age;
   }

   public String getRoleIdName() {
      return roleIdName;
   }

   public void setRoleIdName(String roleIdName) {
      this.roleIdName = roleIdName;
   }

   @Override
   public String toString() {
      return "SysUser{" +
            "id=" + id +
            ", account='" + account + '\'' +
            ", realName='" + realName + '\'' +
            ", password='" + password + '\'' +
            ", sex=" + sex +
            ", birthday=" + birthday +
            ", phone='" + phone + '\'' +
            ", address='" + address + '\'' +
            ", roleId=" + roleId +
            ", createdUserId=" + createdUserId +
            ", createdTime=" + createdTime +
            ", updatedUserId=" + updatedUserId +
            ", updatedTime=" + updatedTime +
            ", age=" + age +
            ", roleIdName='" + roleIdName + '\'' +
            '}';
   }
}

  Database DAO interface SysUserMapper.java

package com.bdqn.dao;

import com.bdqn.pojo.SysUser;

import java.util.List;

/**
 * SysUserMapper Data access layer interface
 *
 * @author Miku
 * @since 2021-05-14
 */
public interface SysUserMapper {

    /**
     * Query user list
     * 
     * @param sysUser
     * @return
     */
    public List<SysUser> selectSysUserList(SysUser sysUser);
}

MyBatis mapping file SysUserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- SysUserMapper Interface MyBatis Mapping file -->
<mapper namespace="com.bdqn.dao.SysUserMapper">
    <!-- When the field information in the database is inconsistent with the attributes of the object, you need to pass the resultMap To map -->
    <resultMap id="SysUserResult" type="SysUser">
        <result property="roleIdName" column="roleName"/>
    </resultMap>

    <!-- Query user list-roleId by null 0 pieces of data were found during query -->
    <select id="selectSysUserList" parameterType="SysUser" resultMap="SysUserResult">
        select u.*, r.roleName
        from t_sys_user u,
             t_sys_role r
        where u.roleId = r.id
          and u.roleId = #{roleId}
          and u.realName like CONCAT('%', #{realName}, '%')
    </select>
</mapper>

Database interface implementation class SysUserMapperImpl.java

package com.bdqn.dao;

import com.bdqn.pojo.SysUser;
import org.mybatis.spring.SqlSessionTemplate;

import java.util.List;

/**
 * SysUserMapper Implementation class (operate the database through SqlSessionTemplate)
 *
 * @author Miku
 * @since 2021-05-14
 */
public class SysUserMapperImpl implements SysUserMapper {

    // The class provided in the mybatis spring package that implements the SqlSession interface is used to operate the database
    private SqlSessionTemplate sqlSession;

    /**
     * Query user list
     *
     * @param sysUser
     * @return
     */
    @Override
    public List<SysUser> selectSysUserList(SysUser sysUser) {
        return sqlSession.getMapper(SysUserMapper.class).selectSysUserList(sysUser);
    }

    public void setSqlSession(SqlSessionTemplate sqlSession) {
        this.sqlSession = sqlSession;
    }
}

  Service layer interface SysUserService.java

package com.bdqn.service;


import com.bdqn.pojo.SysUser;

import java.util.List;

/**
 * SysUserService Service layer interface
 *
 * @author Miku
 * @since 2021-05-14
 */
public interface SysUserService {

    /**
     * Get user list
     *
     * @param sysUser
     * @return
     */
    public List<SysUser> getList(SysUser sysUser);
}

  Service layer interface implementation class SysUserServiceImpl.java

package com.bdqn.service;

import com.bdqn.dao.SysUserMapper;
import com.bdqn.pojo.SysUser;

import java.util.List;

/**
 * SysUserService Service layer implementation class
 *
 * @author Miku
 * @since 2021-05-14
 */
public class SysUserServiceImpl implements SysUserService {

    private SysUserMapper sysUserMapper;

    @Override
    public List<SysUser> getList(SysUser sysUser) {
        try {
            return sysUserMapper.selectSysUserList(sysUser);
        } catch (RuntimeException e) {
            e.printStackTrace();
            throw e;
        }
    }

    public void setSysUserMapper(SysUserMapper sysUserMapper) {
        this.sysUserMapper = sysUserMapper;
    }

}

Step 3: configure the MyBatis core configuration file

mybatis-config.xml

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

    <!-- Type alias, configure the basic package, and then Mapper.xml The class name can be abbreviated in lowercase in the file -->
    <typeAliases>
        <package name="com.bdqn.pojo" />
    </typeAliases>

</configuration>

Note: leave the configuration of the data source to Spring. According to the above configuration, comment out the introduction of external data source configuration and MyBatis data source environment configuration.

Step 4 configure the Spring core configuration file

unit testing

 applicationContext.xml

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

    <!-- Configure database connection pool -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/cvs_db?serverTimezone=UTC&amp;characterEncoding=utf-8"/>
        <property name="username" value="root"/>
        <property name="password" value="lilibo"/>
    </bean>

    <!-- to configure SqlSessionFactoryBean -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- Reference database configuration<bean id="dataSource"> -->
        <property name="dataSource" ref="dataSource"/>
        <!-- introduce MyBatis configuration file -->
        <property name="configLocation" value="classpath:mybatis-config.xml" />
        <!--to configure SQL Map file information-->
        <property name="mapperLocations">
            <list>
                <value>classpath:com/bdqn/dao/**/*.xml</value>
            </list>
        </property>
    </bean>

    <!-- to configure SqlSessionTemplate -->
    <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
        <!--take sqlSessionFactory Inject into sqlSessionTemplate Medium( ref="sqlSessionFactory" ==> <bean id="sqlSessionFactory">)-->
        <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>
    <!-- to configure DAO -->
    <bean id="sysUserMapper" class="com.bdqn.dao.SysUserMapperImpl">
        <property name="sqlSession" ref="sqlSessionTemplate" />
    </bean>
    <!-- to configure Service -->
    <bean id="sysUserService" class="com.bdqn.service.SysUserServiceImpl">
        <property name="sysUserMapper" ref="sysUserMapper" />
    </bean>
</beans>

  unit testing

Create SysUserTest.java under the project test  

package com.bdqn;

import com.bdqn.pojo.SysUser;
import com.bdqn.service.SysUserService;
import org.apache.log4j.Logger;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.ArrayList;
import java.util.List;

/**
 * SysUser Business test
 *
 * @author Miku
 * @since 2021-05-14
 */
public class SysUserTest {

    private Logger logger = Logger.getLogger(SysUserTest.class);

    @Before
    public void setUp() throws Exception {
    }

    @Test
    public void testGetUserList() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        SysUserService userService = (SysUserService) ctx.getBean("sysUserService");
        List<SysUser> userList = new ArrayList<SysUser>();
        SysUser sysUser = new SysUser();
        sysUser.setRealName("Zhao");
        sysUser.setRoleId(2);
        userList = userService.getList(sysUser);

        for (SysUser userResult : userList) {
            logger.debug("testGetUserList account: "
                    + userResult.getAccount() + " and realName: "
                    + userResult.getRealName() + " and roleId: "
                    + userResult.getRoleId() + " and roleName: "
                    + userResult.getRoleIdName() + " and address: "
                    + userResult.getAddress());
        }
    }

}

[test results]

C:\Java\jdk1.8.0_261\bin\java.exe -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2020.1.3\lib\idea_rt.jar=58319:C:\Program Files\JetBrains\IntelliJ IDEA 2020.1.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\JetBrains\IntelliJ IDEA 2020.1.3\lib\idea_rt.jar;C:\Program Files\JetBrains\IntelliJ IDEA 2020.1.3\plugins\junit\lib\junit5-rt.jar;C:\Program Files\JetBrains\IntelliJ IDEA 2020.1.3\plugins\junit\lib\junit-rt.jar;C:\Java\jdk1.8.0_261\jre\lib\charsets.jar;C:\Java\jdk1.8.0_261\jre\lib\deploy.jar;C:\Java\jdk1.8.0_261\jre\lib\ext\access-bridge-64.jar;C:\Java\jdk1.8.0_261\jre\lib\ext\cldrdata.jar;C:\Java\jdk1.8.0_261\jre\lib\ext\dnsns.jar;C:\Java\jdk1.8.0_261\jre\lib\ext\jaccess.jar;C:\Java\jdk1.8.0_261\jre\lib\ext\jfxrt.jar;C:\Java\jdk1.8.0_261\jre\lib\ext\localedata.jar;C:\Java\jdk1.8.0_261\jre\lib\ext\nashorn.jar;C:\Java\jdk1.8.0_261\jre\lib\ext\sunec.jar;C:\Java\jdk1.8.0_261\jre\lib\ext\sunjce_provider.jar;C:\Java\jdk1.8.0_261\jre\lib\ext\sunmscapi.jar;C:\Java\jdk1.8.0_261\jre\lib\ext\sunpkcs11.jar;C:\Java\jdk1.8.0_261\jre\lib\ext\zipfs.jar;C:\Java\jdk1.8.0_261\jre\lib\javaws.jar;C:\Java\jdk1.8.0_261\jre\lib\jce.jar;C:\Java\jdk1.8.0_261\jre\lib\jfr.jar;C:\Java\jdk1.8.0_261\jre\lib\jfxswt.jar;C:\Java\jdk1.8.0_261\jre\lib\jsse.jar;C:\Java\jdk1.8.0_261\jre\lib\management-agent.jar;C:\Java\jdk1.8.0_261\jre\lib\plugin.jar;C:\Java\jdk1.8.0_261\jre\lib\resources.jar;C:\Java\jdk1.8.0_261\jre\lib\rt.jar;D:\Works\ktjiaoyu\s3-spring-mybatis\target\test-classes;D:\Works\ktjiaoyu\s3-spring-mybatis\target\classes;D:\Source\maven\repository\junit\junit\4.13\junit-4.13.jar;D:\Source\maven\repository\org\springframework\spring-aop\5.3.4\spring-aop-5.3.4.jar;D:\Source\maven\repository\org\springframework\spring-beans\5.3.4\spring-beans-5.3.4.jar;D:\Source\maven\repository\org\springframework\spring-context\5.3.4\spring-context-5.3.4.jar;D:\Source\maven\repository\org\springframework\spring-core\5.3.4\spring-core-5.3.4.jar;D:\Source\maven\repository\org\springframework\spring-jcl\5.3.4\spring-jcl-5.3.4.jar;D:\Source\maven\repository\org\springframework\spring-expression\5.3.4\spring-expression-5.3.4.jar;D:\Source\maven\repository\aopalliance\aopalliance\1.0\aopalliance-1.0.jar;D:\Source\maven\repository\org\aspectj\aspectjweaver\1.9.6\aspectjweaver-1.9.6.jar;D:\Source\maven\repository\org\hamcrest\hamcrest-core\2.2\hamcrest-core-2.2.jar;D:\Source\maven\repository\org\hamcrest\hamcrest\2.2\hamcrest-2.2.jar;D:\Source\maven\repository\log4j\log4j\1.2.17\log4j-1.2.17.jar;D:\Source\maven\repository\org\mybatis\mybatis\3.5.6\mybatis-3.5.6.jar;D:\Source\maven\repository\mysql\mysql-connector-java\8.0.18\mysql-connector-java-8.0.18.jar;D:\Source\maven\repository\com\google\protobuf\protobuf-java\3.6.1\protobuf-java-3.6.1.jar;D:\Source\maven\repository\org\mybatis\mybatis-spring\2.0.6\mybatis-spring-2.0.6.jar;D:\Source\maven\repository\org\springframework\spring-jdbc\5.3.4\spring-jdbc-5.3.4.jar;D:\Source\maven\repository\org\springframework\spring-tx\5.3.4\spring-tx-5.3.4.jar;D:\Source\maven\repository\commons-dbcp\commons-dbcp\1.4\commons-dbcp-1.4.jar;D:\Source\maven\repository\commons-pool\commons-pool\1.6\commons-pool-1.6.jar" com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 com.bdqn.SysUserTest,testGetUserList 
[DEBUG] 2021-05-17 01:14:18,457 com.bdqn.SysUserTest - testGetUserList account: zhaogang and realName: Zhao Gang and roleId: 2 and roleName: shopowner and address: Guanzhuang crescent community, Fengtai District, Beijing 
 
Process finished with exit code 0 

So far, Spring has integrated MyBatis configuration.

Optimization 1: replace SqlSessionTemplate with SqlSessionDaoSupport

In addition to directly using SqlSessionTempIate to obtain the SqlSession instance processing data, mybatis spring also provides the SqlSessionDaoSupport class to simplify the SqlSessionTemplate. Obtain the SqlSession object through this.getSqlSession() to perform database operations.

Create SysUserMapperDaoSupportImpl.java under com.bdqn.dao package

package com.bdqn.dao;

import com.bdqn.pojo.SysUser;
import org.mybatis.spring.support.SqlSessionDaoSupport;

import java.util.List;

/**
 * Inherit the implementation of SqlSessionDaoSupport abstract class (obtain the SqlSession object through getSqlSession() method and operate the database)
 *
 * @author Miku
 * @since 2021-05-14
 */
public class SysUserMapperDaoSupportImpl extends SqlSessionDaoSupport implements SysUserMapper {

    /**
     * Query user list
     *
     * @param sysUser
     * @return
     */
    @Override
    public List<SysUser> selectSysUserList(SysUser sysUser) {
        return this.getSqlSession().getMapper(SysUserMapper.class).selectSysUserList(sysUser);
    }

}

Note: after inheriting SqlSessionDaoSupport, you do not need to define the attribute SqlSession. SqlSession directly obtains the SqlSession object through this.getSqlSession() to perform database operations.

Modify the corresponding configuration of applicationContext.xml  

<!-- to configure DAO 1, Use SqlSessionTemplate edition -->
<!-- MyBatis-Spring Provided SqlSessionTemplate Class, inherited SqlSession Interface, operating database -->
<!--<bean id="sysUserMapper" class="com.bdqn.dao.SysUserMapperImpl">
    <property name="sqlSession" ref="sqlSessionTemplate" />
</bean>-->
<!-- to configure DAO 2, Use SqlSessionDaoSupport edition -->
<!-- MyBatis-Spring Provided through this.getSqlSession()Method obtain SqlSession The instance operates the database and does not need to be defined SqlSession Object, which is more convenient to use -->
<bean id="sysUserMapper" class="com.bdqn.dao.SysUserMapperDaoSupportImpl">
    <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

  Note: note that the first method is the SqlSessionTemplate version. Replace it with the second method and use the SqlSessionDaoSupport version.

Optimization II using MapperFactoryBean injection mapper  

The MapperFactoryBean provided by mybatis spring can generate the implementation class of the mapper in the way of configuration and inject it into the business component without writing the DAO implementation class.

  Modify the corresponding configuration of applicationContext.xml

<!-- to configure SqlSessionFactoryBean -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!-- Reference database configuration<bean id="dataSource"> -->
    <property name="dataSource" ref="dataSource"/>
    <!-- introduce MyBatis configuration file -->
    <property name="configLocation" value="classpath:mybatis-config.xml" />
    <!-- to configure SQL Map file information -->
    <!-- use MyBatis-Spring Provided MapperFactoryBean If the mapper corresponds to SQL The mapping file is the same as the class path of the mapper and can be automatically mapped MapperFactoryBean Resolve. In this case, configure SessionFactoryBean You don't have to specify SQL The location of the mapping file. Conversely, if the class path of the mapper is different from that of the mapping file, you still need to specify the location of the mapping file explicitly -->
    <!--<property name="mapperLocations">
        <list>
            <value>classpath:com/bdqn/dao/**/*.xml</value>
        </list>
    </property>-->
</bean>

<!-- to configure DAO 1, Use SqlSessionTemplate edition -->
<!-- MyBatis-Spring Provided SqlSessionTemplate Class, inherited SqlSession Interface, operating database -->
<!--<bean id="sysUserMapper" class="com.bdqn.dao.SysUserMapperImpl">
    <property name="sqlSession" ref="sqlSessionTemplate" />
</bean>-->
<!-- to configure DAO 2, Use SqlSessionDaoSupport edition -->
<!-- MyBatis-Spring Provided through this.getSqlSession()Method obtain SqlSession The instance operates the database and does not need to be defined SqlSession Object, which is more convenient to use -->
<!--<bean id="sysUserMapper" class="com.bdqn.dao.SysUserMapperDaoSupportImpl">
    <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>-->
<!-- to configure DAO 3, Use MapperFactoryBean edition -->
<!-- MyBatis-Spring Provided MapperFactoryBean It can generate the implementation class of the mapper in the way of configuration and inject it into the business component without writing DAO Implementation class -->
<bean id="sysUserMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
    <property name="sqlSessionFactory" ref="sqlSessionFactory" />
    <property name="mapperInterface" value="com.bdqn.dao.SysUserMapper" />
</bean>

Note: after configuration by using MapperFactoryBean (the third method), you do not need to write the implementation class of DAO, but only the mapping file of Mapper.xml.

Optimization 3: using mappercannerconfig to inject mapper  

Configuring MapperFactoryBean for mappers greatly simplifies the coding of DAO modules. However, if there are many mappers, there will be many corresponding configuration items. In order to simplify the configuration workload, mybatis spring provides mappercannerconfigurer, which can scan the interfaces in the specified package and directly register them as MapperFactoryBean.  

Modify the corresponding configuration of applicationContext.xml  

<!-- to configure DAO 1, Use SqlSessionTemplate edition -->
<!-- MyBatis-Spring Provided SqlSessionTemplate Class, inherited SqlSession Interface, operating database -->
<!--<bean id="sysUserMapper" class="com.bdqn.dao.SysUserMapperImpl">
    <property name="sqlSession" ref="sqlSessionTemplate" />
</bean>-->
<!-- to configure DAO 2, Use SqlSessionDaoSupport edition -->
<!-- MyBatis-Spring Provided through this.getSqlSession()Method obtain SqlSession The instance operates the database and does not need to be defined SqlSession Object, which is more convenient to use -->
<!--<bean id="sysUserMapper" class="com.bdqn.dao.SysUserMapperDaoSupportImpl">
    <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>-->
<!-- to configure DAO 3, Use MapperFactoryBean edition -->
<!-- MyBatis-Spring Provided MapperFactoryBean It can generate the implementation class of the mapper in the way of configuration and inject it into the business component without writing DAO Implementation class -->
<!--<bean id="sysUserMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
    <property name="sqlSessionFactory" ref="sqlSessionFactory" />
    <property name="mapperInterface" value="com.bdqn.dao.SysUserMapper" />
</bean>-->
<!-- to configure DAO 4, Use MapperScannerConfig edition -->
<!-- MyBatis-Spring Provided MapperScannerConfigurer All interfaces under the benchmark package (including sub packages at all levels) will be recursively scanned. If they are in SQL If they are defined in the mapping file, they will be dynamically registered as the implementation class of the mapper, and the implementation class of the mapper can be generated in batch -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.bdqn.dao" />
</bean>

  Note: after configuration by using mappercannerconfigurer (the fourth method), the specified basePackage package is called the benchmark package, and mappercannerconfigurer will recursively scan all interfaces under the benchmark package (including sub packages at all levels). If they have been defined in the SQL mapping file, they will be dynamically registered as mapper implementation classes to generate mapper implementation classes in batch.

Full applicationContext.xml configuration

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

    <!-- Configure database connection pool -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/cvs_db?serverTimezone=UTC&amp;characterEncoding=utf-8"/>
        <property name="username" value="root"/>
        <property name="password" value="lilibo"/>
    </bean>

    <!-- to configure SqlSessionFactoryBean -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- Reference database configuration<bean id="dataSource"> -->
        <property name="dataSource" ref="dataSource"/>
        <!-- introduce MyBatis configuration file -->
        <property name="configLocation" value="classpath:mybatis-config.xml" />
        <!-- to configure SQL Map file information -->
        <!-- use MyBatis-Spring Provided MapperFactoryBean If the mapper corresponds to SQL The mapping file is the same as the class path of the mapper and can be automatically mapped MapperFactoryBean Analysis. In this case, configure SessionFactoryBean You don't have to specify SQL The location of the mapping file. Conversely, if the class path of the mapper is different from that of the mapping file, you still need to specify the location of the mapping file -->
        <!--<property name="mapperLocations">
            <list>
                <value>classpath:com/bdqn/dao/**/*.xml</value>
            </list>
        </property>-->
    </bean>

    <!-- to configure SqlSessionTemplate -->
    <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
        <!-- take sqlSessionFactory Inject into sqlSessionTemplate Medium( ref="sqlSessionFactory" ==> <bean id="sqlSessionFactory">) -->
        <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

    <!-- to configure DAO 1, Use SqlSessionTemplate edition -->
    <!-- MyBatis-Spring Provided SqlSessionTemplate Class, inherited SqlSession Interface, operating database -->
    <!--<bean id="sysUserMapper" class="com.bdqn.dao.SysUserMapperImpl">
        <property name="sqlSession" ref="sqlSessionTemplate" />
    </bean>-->
    <!-- to configure DAO 2, Use SqlSessionDaoSupport edition -->
    <!-- MyBatis-Spring Provided through this.getSqlSession()Method obtain SqlSession The instance operates the database and does not need to be defined SqlSession Object, which is more convenient to use -->
    <!--<bean id="sysUserMapper" class="com.bdqn.dao.SysUserMapperDaoSupportImpl">
        <property name="sqlSessionFactory" ref="sqlSessionFactory" />
    </bean>-->
    <!-- to configure DAO 3, Use MapperFactoryBean edition -->
    <!-- MyBatis-Spring Provided MapperFactoryBean It can generate the implementation class of the mapper in the way of configuration and inject it into the business component without writing DAO Implementation class -->
    <!--<bean id="sysUserMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
        <property name="sqlSessionFactory" ref="sqlSessionFactory" />
        <property name="mapperInterface" value="com.bdqn.dao.SysUserMapper" />
    </bean>-->
    <!-- to configure DAO 4, Use MapperScannerConfig edition -->
    <!-- MyBatis-Spring Provided MapperScannerConfigurer All interfaces under the benchmark package (including sub packages at all levels) will be recursively scanned. If they are in SQL If they are defined in the mapping file, they will be dynamically registered as the implementation class of the mapper, and the implementation class of the mapper can be generated in batch -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.bdqn.dao" />
    </bean>
    <!-- to configure Service -->
    <bean id="sysUserService" class="com.bdqn.service.SysUserServiceImpl">
        <property name="sysUserMapper" ref="sysUserMapper" />
    </bean>
</beans>

  Annotation method uses annotations to simplify business layer configuration

When a mapper is registered to the Spring container, the Spring framework will name it according to its interface name, which is a lowercase, non fully qualified name by default. For example, a component of type sysusermapper is named sysusermapper by default. During development, you can use @ Autowired or @ Resource annotations to implement dependency injection on business components to simplify the configuration of business components  

  Modify the SysUserServiceImpl.java file with annotations

package com.bdqn.service;

import com.bdqn.dao.SysUserMapper;
import com.bdqn.pojo.SysUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * SysUserService Service layer implementation class
 *
 * @author Miku
 * @since 2021-05-14
 */
@Service("sysUserService") // Bean registered as a Service layer
public class SysUserServiceImpl implements SysUserService {

    @Autowired // @Resource / / inject beans of DAO layer by annotation
    private SysUserMapper sysUserMapper;

    @Override
    public List<SysUser> getList(SysUser sysUser) {
        try {
            return sysUserMapper.selectSysUserList(sysUser);
        } catch (RuntimeException e) {
            e.printStackTrace();
            throw e;
        }
    }

}

Summary:

Annotations for Spring registered beans include

  1. @Component: used to label the class as a Bean
  2. @Repository: used to label DAO class as a Bean
  3. @Service: used to mark the business class as a Bean
  4. @Controller: used to label the controller class as a Bean

Note: for annotation configuration, you need to modify the Spring configuration file, remove the configuration of business layer beans, and add the business beans defined by scanning annotations  

Modify applicationContext.xml configuration

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

    <!-- Configure database connection pool -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/cvs_db?serverTimezone=UTC&amp;characterEncoding=utf-8"/>
        <property name="username" value="root"/>
        <property name="password" value="lilibo"/>
    </bean>

    <!-- to configure SqlSessionFactoryBean -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- Reference database configuration<bean id="dataSource"> -->
        <property name="dataSource" ref="dataSource"/>
        <!-- introduce MyBatis configuration file -->
        <property name="configLocation" value="classpath:mybatis-config.xml" />
        <!-- to configure SQL Map file information -->
        <!-- use MyBatis-Spring Provided MapperFactoryBean If the mapper corresponds to SQL The mapping file is the same as the class path of the mapper and can be automatically mapped MapperFactoryBean Resolve. In this case, configure SessionFactoryBean You don't have to specify SQL The location of the mapping file. Conversely, if the class path of the mapper is different from that of the mapping file, you still need to specify the location of the mapping file explicitly -->
        <!--<property name="mapperLocations">
            <list>
                <value>classpath:com/bdqn/dao/**/*.xml</value>
            </list>
        </property>-->
    </bean>

    <!-- to configure SqlSessionTemplate -->
    <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
        <!-- take sqlSessionFactory Inject into sqlSessionTemplate Medium( ref="sqlSessionFactory" ==> <bean id="sqlSessionFactory">) -->
        <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

    <!-- to configure DAO 1, Use SqlSessionTemplate edition -->
    <!-- MyBatis-Spring Provided SqlSessionTemplate Class, inherited SqlSession Interface, operating database -->
    <!--<bean id="sysUserMapper" class="com.bdqn.dao.SysUserMapperImpl">
        <property name="sqlSession" ref="sqlSessionTemplate" />
    </bean>-->
    <!-- to configure DAO 2, Use SqlSessionDaoSupport edition -->
    <!-- MyBatis-Spring Provided through this.getSqlSession()Method obtain SqlSession The instance operates the database and does not need to be defined SqlSession Object, which is more convenient to use -->
    <!--<bean id="sysUserMapper" class="com.bdqn.dao.SysUserMapperDaoSupportImpl">
        <property name="sqlSessionFactory" ref="sqlSessionFactory" />
    </bean>-->
    <!-- to configure DAO 3, Use MapperFactoryBean edition -->
    <!-- MyBatis-Spring Provided MapperFactoryBean It can generate the implementation class of the mapper in the way of configuration and inject it into the business component without writing DAO Implementation class -->
    <!--<bean id="sysUserMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
        <property name="sqlSessionFactory" ref="sqlSessionFactory" />
        <property name="mapperInterface" value="com.bdqn.dao.SysUserMapper" />
    </bean>-->
    <!-- to configure DAO 4, Use MapperScannerConfig edition -->
    <!-- MyBatis-Spring Provided MapperScannerConfigurer All interfaces under the benchmark package (including sub packages at all levels) will be recursively scanned. If they are in SQL If they are defined in the mapping file, they will be dynamically registered as the implementation class of the mapper, and the implementation class of the mapper can be generated in batch -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.bdqn.dao" />
    </bean>

    <!-- to configure Service -->
    <!-- Use annotation to configure business Bean Delete the configuration when -->
    <!--<bean id="sysUserService" class="com.bdqn.service.SysUserServiceImpl">
        <property name="sysUserMapper" ref="sysUserMapper" />
    </bean>-->

    <!-- Configure by annotation and define the scanned package -->
    <context:component-scan base-package="com.bdqn.service" />
</beans>

  Rerun the unit tests after each configuration optimization

[test results]  

C:\Java\jdk1.8.0_261\bin\java.exe -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2020.1.3\lib\idea_rt.jar=58319:C:\Program Files\JetBrains\IntelliJ IDEA 2020.1.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\JetBrains\IntelliJ IDEA 2020.1.3\lib\idea_rt.jar;C:\Program Files\JetBrains\IntelliJ IDEA 2020.1.3\plugins\junit\lib\junit5-rt.jar;C:\Program Files\JetBrains\IntelliJ IDEA 2020.1.3\plugins\junit\lib\junit-rt.jar;C:\Java\jdk1.8.0_261\jre\lib\charsets.jar;C:\Java\jdk1.8.0_261\jre\lib\deploy.jar;C:\Java\jdk1.8.0_261\jre\lib\ext\access-bridge-64.jar;C:\Java\jdk1.8.0_261\jre\lib\ext\cldrdata.jar;C:\Java\jdk1.8.0_261\jre\lib\ext\dnsns.jar;C:\Java\jdk1.8.0_261\jre\lib\ext\jaccess.jar;C:\Java\jdk1.8.0_261\jre\lib\ext\jfxrt.jar;C:\Java\jdk1.8.0_261\jre\lib\ext\localedata.jar;C:\Java\jdk1.8.0_261\jre\lib\ext\nashorn.jar;C:\Java\jdk1.8.0_261\jre\lib\ext\sunec.jar;C:\Java\jdk1.8.0_261\jre\lib\ext\sunjce_provider.jar;C:\Java\jdk1.8.0_261\jre\lib\ext\sunmscapi.jar;C:\Java\jdk1.8.0_261\jre\lib\ext\sunpkcs11.jar;C:\Java\jdk1.8.0_261\jre\lib\ext\zipfs.jar;C:\Java\jdk1.8.0_261\jre\lib\javaws.jar;C:\Java\jdk1.8.0_261\jre\lib\jce.jar;C:\Java\jdk1.8.0_261\jre\lib\jfr.jar;C:\Java\jdk1.8.0_261\jre\lib\jfxswt.jar;C:\Java\jdk1.8.0_261\jre\lib\jsse.jar;C:\Java\jdk1.8.0_261\jre\lib\management-agent.jar;C:\Java\jdk1.8.0_261\jre\lib\plugin.jar;C:\Java\jdk1.8.0_261\jre\lib\resources.jar;C:\Java\jdk1.8.0_261\jre\lib\rt.jar;D:\Works\ktjiaoyu\s3-spring-mybatis\target\test-classes;D:\Works\ktjiaoyu\s3-spring-mybatis\target\classes;D:\Source\maven\repository\junit\junit\4.13\junit-4.13.jar;D:\Source\maven\repository\org\springframework\spring-aop\5.3.4\spring-aop-5.3.4.jar;D:\Source\maven\repository\org\springframework\spring-beans\5.3.4\spring-beans-5.3.4.jar;D:\Source\maven\repository\org\springframework\spring-context\5.3.4\spring-context-5.3.4.jar;D:\Source\maven\repository\org\springframework\spring-core\5.3.4\spring-core-5.3.4.jar;D:\Source\maven\repository\org\springframework\spring-jcl\5.3.4\spring-jcl-5.3.4.jar;D:\Source\maven\repository\org\springframework\spring-expression\5.3.4\spring-expression-5.3.4.jar;D:\Source\maven\repository\aopalliance\aopalliance\1.0\aopalliance-1.0.jar;D:\Source\maven\repository\org\aspectj\aspectjweaver\1.9.6\aspectjweaver-1.9.6.jar;D:\Source\maven\repository\org\hamcrest\hamcrest-core\2.2\hamcrest-core-2.2.jar;D:\Source\maven\repository\org\hamcrest\hamcrest\2.2\hamcrest-2.2.jar;D:\Source\maven\repository\log4j\log4j\1.2.17\log4j-1.2.17.jar;D:\Source\maven\repository\org\mybatis\mybatis\3.5.6\mybatis-3.5.6.jar;D:\Source\maven\repository\mysql\mysql-connector-java\8.0.18\mysql-connector-java-8.0.18.jar;D:\Source\maven\repository\com\google\protobuf\protobuf-java\3.6.1\protobuf-java-3.6.1.jar;D:\Source\maven\repository\org\mybatis\mybatis-spring\2.0.6\mybatis-spring-2.0.6.jar;D:\Source\maven\repository\org\springframework\spring-jdbc\5.3.4\spring-jdbc-5.3.4.jar;D:\Source\maven\repository\org\springframework\spring-tx\5.3.4\spring-tx-5.3.4.jar;D:\Source\maven\repository\commons-dbcp\commons-dbcp\1.4\commons-dbcp-1.4.jar;D:\Source\maven\repository\commons-pool\commons-pool\1.6\commons-pool-1.6.jar" com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 com.bdqn.SysUserTest,testGetUserList 
[DEBUG] 2021-05-17 01:14:18,457 com.bdqn.SysUserTest - testGetUserList account: zhaogang and realName: Zhao Gang and roleId: 2 and roleName: shopowner and address: Guanzhuang crescent community, Fengtai District, Beijing 
 
Process finished with exit code 0

  Spring integration MyBatis configuration summary

  1. SqlSessionTemplate mybatis spring provides the SqlSessionTemplate class, inherits the SqlSession interface, and operates the database
  2. Sqlsessiondaosupport mybatis spring provides SqlSession instance operation database through this.getSqlSession() method, which is more convenient to use without defining SqlSession object
  3. MapperFactoryBean mybatis spring provides MapperFactoryBean that can generate the implementation class of the mapper in the way of configuration and inject it into business components without writing DAO implementation classes
  4. Mappercannerconfigurer provided by mappercannerconfig mybatis spring will recursively scan all interfaces under the benchmark package (including sub packages at all levels). If they have been defined in the SQL mapping file, they will be dynamically registered as mapper implementation classes to generate mapper implementation classes in batch

  2, Configure declarative transactions

What is a transaction?  

Transaction is to treat a series of actions as an independent unit of work, which are either completed or ineffective. It is to execute a series of operations as atomicity.  

Transaction has four attributes ACID:   
1. Atomicity

         A transaction is an atomic operation, which consists of a series of actions. The atomicity of a transaction ensures that the actions either complete or fail to work at all

2. Consistency

         Once all the transaction actions are completed, the transaction will be committed. The data and resources are in a consistent state that meets the business rules

3. Isolation

         Multiple transactions may process the same data at the same time, so each transaction should be isolated from other transactions to prevent data corruption

4. Durability

         Once the transaction is completed, no matter what error occurs in the system, the transaction is closed

The results are not affected. Usually, the results of transactions are written to persistent memory

Implement transaction classification:

  • Programming transaction management

         Embed transaction management code into business methods to control transaction commit and rollback

         Disadvantages: additional transaction management code must be included in each transaction operation business logic

  • Declarative transaction management

         In general, it is easier to use than programmatic transactions. The transaction management code is separated from business methods to realize transaction management in a declarative way. Transaction management is regarded as a crosscutting concern and modularized through AOP methods. Spring supports declarative transaction management through the Spring AOP framework

Transaction manager:

         Spring's core transaction management abstraction encapsulates a set of technology independent methods. No matter which transaction management strategy (programmatic or declarative) spring uses, the transaction manager is necessary

How to configure declarative transactions in Spring?

preparation

add(SysUser sysUser) method is added to the SysUserMapper.java interface

package com.bdqn.dao;

import com.bdqn.pojo.SysUser;

import java.util.List;

/**
 * SysUserMapper Data access layer interface
 *
 * @author Miku
 * @since 2021-05-14
 */
public interface SysUserMapper {

    // Omit other code

    /**
     * Save user
     * @param sysUser
     * @return
     */
    public int add(SysUser sysUser);
}

A < insert id = "add" > node is added to the SysUserMapper.xml mapping file

<!-- Save user -->
<insert id="add" parameterType="SysUser">
    insert into t_sys_user (account, realName, password, sex, birthday, phone, address, roleId, createdUserId, createdTime)
    values (#{account}, #{realName}, #{password}, #{sex}, #{birthday}, #{phone}, #{address}, #{roleId}, #{createdUserId}, #{createdTime})
</insert>

add(SysUser sysUser) method is added to SysUserMapperImpl.java implementation class

package com.bdqn.dao;

import com.bdqn.pojo.SysUser;
import org.mybatis.spring.SqlSessionTemplate;

import java.util.List;

/**
 * SysUserMapper Implementation class (operate the database through SqlSessionTemplate)
 *
 * @author Miku
 * @since 2021-05-14
 */
public class SysUserMapperImpl implements SysUserMapper {

    // Omit other code

    /**
     * Save user
     *
     * @param sysUser
     * @return
     */
    @Override
    public int add(SysUser sysUser) {
        return this.getSqlSession().getMapper(SysUserMapper.class).add(sysUser);
    }

}

add(SysUser sysUser) method is added to the SysUserService.java interface

package com.bdqn.service;


import com.bdqn.pojo.SysUser;

import java.util.List;

/**
 * SysUserService Service layer interface
 *
 * @author Miku
 * @since 2021-05-14
 */
public interface SysUserService {

    // Omit other code

    /**
     * Save user
     *
     * @param sysUser
     * @return
     */
    public boolean add(SysUser sysUser);
}

add(SysUser sysUser) method is added to SysUserServiceImpl.java implementation class

package com.bdqn.service;

import com.bdqn.dao.SysUserMapper;
import com.bdqn.pojo.SysUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * SysUserService Service layer implementation class
 *
 * @author Miku
 * @since 2021-05-14
 */
@Service("sysUserService") // Bean registered as a Service layer
public class SysUserServiceImpl implements SysUserService {

    @Autowired // @Resource / / inject beans of DAO layer by annotation
    private SysUserMapper sysUserMapper;

    /**
     * Save user
     *
     * @param sysUser
     * @return
     */
    // @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public boolean add(SysUser sysUser) {
        // Simulate two operations on the database in a transaction. When an exception occurs after the first operation is completed, the second operation is not executed, and the first operation will also be rolled back. Both operations were either unsuccessful or successful at the same time
        sysUserMapper.add(sysUser);
        int num = 5 / 0; // Exception thrown in simulator
        // if (sysUser != null) {
            // Throw exception manually
            // throw new SendEMailException("send notification email exception");
        // }
        sysUserMapper.add(sysUser);
        return true;
    }

}

testAddUser method is added to SysUserTest.java test class

package com.bdqn;

import com.bdqn.pojo.SysUser;
import com.bdqn.service.SysUserService;
import org.apache.log4j.Logger;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * SysUser Business test
 *
 * @author Miku
 * @since 2021-05-14
 */
public class SysUserTest {

    // Omit other code

    /**
     * Test save user
     */
    @Test
    public void testAddUser() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        SysUserService userService = (SysUserService) ctx.getBean("sysUserService");
        SysUser user = new SysUser();
        user.setAccount("test001");
        user.setRealName("Test user 001");
        user.setPassword("1234567");
        Date birthday = null;
        try {
            birthday = new SimpleDateFormat("yyyy-MM-dd").parse("2021-05-17");
        } catch (ParseException e) {
            e.printStackTrace();
        }
        user.setBirthday(birthday);
        user.setAddress("Address test");
        user.setSex(1);
        user.setPhone("13688783697");
        user.setRoleId(1);
        user.setCreatedUserId(1);
        user.setCreatedTime(new Date());
        boolean isok = userService.add(user);
    
        logger.debug("testAdd result: " + isok);
    }

}

Method 1: write configuration for transaction configuration

Step 1: import namespace

This step can be automatically imported in IDEA by using the Alt+Enter shortcut when writing the configuration node.

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans.xsd 
       http://www.springframework.org/schema/context 
       https://www.springframework.org/schema/context/spring-context.xsd 
       http://www.springframework.org/schema/tx 
       http://www.springframework.org/schema/tx/spring-tx.xsd 
       http://www.springframework.org/schema/aop 
       https://www.springframework.org/schema/aop/spring-aop.xsd">

The second step is to define the transaction manager

         The transaction manager provides comprehensive support and unified management for transaction processing, which is equivalent to the role of enhanced processing in AOP. The configuration method is as follows:

applicationContext.xml

<!-- Declarative transaction configuration -->

<!-- First, define the transaction manager (used here is JDBC Transaction manager, in addition to Java Primordial API Transaction manager JPA Transaction manager Hibernate Transaction manager, etc.) -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- Injection data source -->
    <property name="dataSource" ref="dataSource" />
</bean>

The transaction manager class DataSourceTransactionManager provided by the Spring framework is used here. It should be noted that when configuring the DataSourceTransactionManager, you should inject predefined data source components into it.

In addition, there are the following transaction configuration methods in different programming environments:

<!-- Define transaction manager Hibernate affair -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- Define transaction manager JPA affair -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- Define transaction manager Java Primordial API affair -->
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
    <property name="transactionManagerName" value="java:/TransactionManager" />
</bean>

Step 3: set transaction attributes

The transaction manager can specify specific transaction rules for different business methods by setting transaction attributes.

applicationContext.xml

<!-- Declarative transaction configuration, which specifies specific transaction rules for different business methods( transaction-manager The default value of the property is transactionManager. That is, if the transaction manager Bean The name is transactionManager,You can not specify the attribute value) -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <!-- Specify the properties of the transaction according to the method name, with an asterisk(*)Represents a wildcard -->
        <tx:method name="*"/>
        <!-- propagation Configure transaction propagation behavior -->
        <tx:method name="purchase" propagation="REQUIRES_NEW"/>
        <!-- isolation Configure the isolation level of the transaction -->
        <tx:method name="update*" isolation="SERIALIZABLE"/>
        <!-- rollback-for The configuration transaction encountered an exception and must be rolled back; no-rollback-for The configuration transaction encountered an exception and will not be rolled back -->
        <tx:method name="add*" rollback-for="java.io.IOException" no-rollback-for="com.bdqn.common.SendEMailException"/>
        <!-- read-only Configure transaction read-only properties -->
        <tx:method name="find*" read-only="true"/>
        <!-- timeout Configure the timeout attribute of the transaction -->
        <tx:method name="get*" timeout="3"/>
    </tx:attributes>
</tx:advice>

Note: here, the transaction enhancement is configured through the < TX: advice > tag. The default value of the transaction manager attribute is transactionManager. That is, if the defined transaction manager Bean name is transactionManager, the attribute value can not be specified.

Transaction propagation mechanism, isolation level and other attributes can be configured in the < TX: attributes > tag. These attributes are set through the < TX: method > tag under the < TX: attributes > tag. The name attribute in the < TX: method > tag is required to specify the matching method, wildcard (*) Indicates that any character matches, and other attributes are optional. The specific features and usage are shown in the table below:

Transaction properties

explain

name

Which methods need transaction control and support * wildcards

propagation

Transaction propagation mechanism (generally, the first and second types are used more)

  1. REQUIRED if a transaction is running, the current method will run in the transaction. Otherwise, a new transaction will be started and run in its own transaction. The default propagation behavior is
  2. REQUIRED_NEW the current method must start a new transaction and run within its own transaction. If a transaction is running, it will be suspended
  3. SUPPORTS if a transaction is running, the current method will run in the transaction. Otherwise, it can not run in the transaction
  4. NOT_SUPPORTED indicates that the method should not run in a transaction. If there is a current transaction, the current transaction will be suspended while the method is running. If you use JTATransactionManager, you need to access TransactionManager
  5. The current method of MANDATORY must run inside the transaction. If there is no running transaction, an exception will be thrown
  6. The current method of NEVER should not run in a transaction. If there is a running transaction, an exception will be thrown
  7. NESTED if a transaction is running, the current method should run within the NESTED transaction of the transaction. The NESTED transaction can be committed or rolled back independently of the current transaction. If the current transaction does not exist, its behavior is the same as that of deployment_required

isolation

At the transaction isolation level, concurrent transactions can cause the following three types of problems

  • Dirty reading occurs when one transaction reads another transaction and overwrites uncommitted data. If the overwritten data is rolled back, the data obtained by the first transaction is invalid
  • Cannot be read repeatedly. When the same transaction executes the same query twice or more, different data will be obtained each time. Generally, another concurrent transaction is updated during the two queries
  • The first transaction reads some data. At this time, the second transaction inserts some new data into the table. At this time, if the first transaction reads the same data, several more rows will be added

The difference between non repeatable reading and unreal reading: non repeatable reading focuses on the modification of the same data, while unreal reading is deletion or addition

Theoretically, transactions should be completely isolated to avoid problems caused by concurrent transactions, but this may have a great impact on performance, because transactions must be carried out in order. Therefore, in actual development, transactions will run at a relatively low isolation level in order to improve performance.

The isolation level of transactions in Spring can be specified through the isolation attribute:

  1. DEFAULT uses the DEFAULT isolation level of the underlying database. For most databases, the DEFAULT isolation level is read_committed
  2. Read_committed only allows transactions to read changes committed by other transactions, which can avoid dirty reading, but non repeatable reading and phantom reading problems may still occur
  3. Read_uncommitted allows transactions to read changes that are not committed by other transactions. It is the most efficient. Dirty reads, non repeatable reads, and phantom reads may occur
  4. REPEATABLE_READ ensures that transactions can read the same value from a field multiple times. Other transactions are prohibited from updating this field during the duration of this transaction
  5. SERIALIZABLE ensures that transactions can read the same rows from a table. During the duration of this transaction, other transactions are prohibited from inserting, updating and deleting the table. All concurrency problems can be avoided, which is the safest, but the performance is relatively low

Note: the isolation level of transactions needs the support of the underlying database engine, not the application or framework

Oracle supports two transaction isolation levels: read_committed and serial

MySQL supports four transaction isolation levels: READ_COMMITED,READ_UNCOMMITED,REPEATABLE_READ,SERIALIZABLE

timeout

Transaction timeout

The maximum time allowed for a transaction to run, in seconds (s), is automatically rolled back after a given time to prevent the transaction from taking too long to affect the system performance. This attribute needs the support of the underlying implementation. The default value is - 1, which means no timeout

readonly

Boolean value, whether it is a read-only transaction

  1. If true, tell the database that this transaction is read-only. Database optimization will improve performance to a certain extent, so as long as it is a query method, it is recommended to set it to true
  2. If it is false (the default value), it indicates the transaction to be committed. It is suggested to add, modify and delete

rollback-for

Exception type that can trigger rollback

By default, the Spring framework identifies the transaction rollback only when the RuntimeException is thrown. You can specify the exception that needs to rollback the transaction through the fully qualified class name. Multiple class names are separated by English commas (,)

no-rollback-for

Exception type that does not trigger rollback

By default, CheckedException in the Spring framework will not trigger transaction rollback. You can specify the exceptions that need to roll back the transaction through the fully qualified class name. Multiple class names are separated by English commas

Step 4: define the transaction aspect

By defining facets, transaction rules can be applied to specified methods.

applicationContext.xml

<!-- Define facets to associate transaction pointcuts with transaction attributes -->
<aop:config>
    <!-- Define pointcuts expression="execution(* com.bdqn.service..*.*(..))" first[*]Represents any return value type;[com.bdqn.service..]Representative matching com.bdqn.service Package and its sub packages;[*.*(..)]Represents all methods that match all classes in the package -->
    <aop:pointcut id="serviceMethod" expression="execution(* com.bdqn.service..*.*(..))"/>
    <!-- Associate pointcuts with transaction attributes -->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod" />
</aop:config>

Note: here, the transaction attribute component set by the < TX: advice > tag is referenced through the advice ref attribute of the < AOP: advisor > tag.

So far, the declarative transaction of the Spring framework is configured through the above four steps.

applicationContext.xml declarative transaction complete configuration

<!-- Declarative transaction configuration -->

<!-- First, define the transaction manager (used here is JDBC Transaction manager, in addition to Java Primordial API Transaction manager JPA Transaction manager Hibernate Transaction manager, etc.) -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- Injection data source -->
    <property name="dataSource" ref="dataSource" />
</bean>

<!-- Configure transaction attributes. Method 1: configure transactions by writing configuration -->

<!-- Declarative transaction configuration, which specifies specific transaction rules for different business methods( transaction-manager The default value of the property is transactionManager. That is, if the transaction manager Bean The name is transactionManager,You can not specify the attribute value) -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <!-- Specify the properties of the transaction according to the method name, with an asterisk(*)Represents a wildcard -->
        <tx:method name="*"/>
        <!-- propagation Configure transaction propagation behavior -->
        <tx:method name="purchase" propagation="REQUIRES_NEW"/>
        <!-- isolation Configure the isolation level of the transaction -->
        <tx:method name="update*" isolation="SERIALIZABLE"/>
        <!-- rollback-for The configuration transaction encountered an exception and must be rolled back; no-rollback-for The configuration transaction encountered an exception and will not be rolled back -->
        <tx:method name="add*" rollback-for="java.io.IOException" no-rollback-for="com.bdqn.common.SendEMailException"/>
        <!-- read-only Configure transaction read-only properties -->
        <tx:method name="find*" read-only="true"/>
        <!-- timeout Configure the timeout attribute of the transaction -->
        <tx:method name="get*" timeout="3"/>
    </tx:attributes>
</tx:advice>
<!-- Define facets to associate transaction pointcuts with transaction attributes -->
<aop:config>
    <!-- Define pointcuts expression="execution(* com.bdqn.service..*.*(..))" first[*]Represents any return value type;[com.bdqn.service..]Representative matching com.bdqn.service Package and its sub packages;[*.*(..)]Represents all methods that match all classes in the package -->
    <aop:pointcut id="serviceMethod" expression="execution(* com.bdqn.service..*.*(..))"/>
    <!-- Associate pointcuts with transaction attributes -->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod" />
</aop:config>

Among them, com.bdqn.common.SendEMailException is a custom exception type, which indicates an exception occurred when sending mail. The code is as follows:

SendEMailException.java

package com.bdqn.common;

/**
 * Sending EMail exception
 *
 * @author Miku
 * @since 2021-05-14
 */
public class SendEMailException extends RuntimeException {

   private static final long serialVersionUID = 1L;

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

   public SendEMailException(Throwable cause) {
      super(cause);
   }

   public SendEMailException(String message, Throwable cause) {
      super(message, cause);
   }

   public SendEMailException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
      super(message, cause, enableSuppression, writableStackTrace);
   }

}

Method 2: transaction configuration using annotations

In addition to using configuration files to handle transactions, the Spring framework also supports annotation with a small amount of configuration to handle declarative transactions. Compared with the pure configuration file method, the code written in this method is much simpler.

applicationContext.xml uses annotations for transaction configuration

<!-- Declarative transaction configuration -->

<!-- First, define the transaction manager (used here is JDBC Transaction manager, in addition to Java Primordial API Transaction manager JPA Transaction manager Hibernate Transaction manager, etc.) -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- Injection data source -->
    <property name="dataSource" ref="dataSource" />
</bean>

<!-- Enable the annotation transaction processing function. Method 2: use annotations for transaction configuration (note that the transaction manager still needs to be configured) -->
<tx:annotation-driven />

Once the configuration file is written, you can use the @ Transactional annotation in your code to process transactions

SysUserServiceImpl.java

package com.bdqn.service;

import com.bdqn.dao.SysUserMapper;
import com.bdqn.pojo.SysUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * SysUserService Service layer implementation class
 *
 * @author Miku
 * @since 2021-05-14
 */
@Transactional
@Service("sysUserService")
public class SysUserServiceImpl implements SysUserService {

    @Autowired // @Resource
    private SysUserMapper sysUserMapper;

    @Transactional(propagation = Propagation.SUPPORTS)
    @Override
    public List<SysUser> getList(SysUser sysUser) {
        try {
            return sysUserMapper.selectSysUserList(sysUser);
        } catch (RuntimeException e) {
            e.printStackTrace();
            throw e;
        }
    }

    /**
     * Save user
     *
     * @param sysUser
     * @return
     */
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public boolean add(SysUser sysUser) {
        // Simulate two operations on the database in a transaction. When an exception occurs after the first operation is completed, the second operation is not executed, and the first operation will also be rolled back. Both operations are either unsuccessful or successful at the same time
        sysUserMapper.add(sysUser);
        int num = 5 / 0; // Exception thrown in simulator
        /*if (sysUser != null) {
            // Throw exception manually
            throw new SendEMailException("Sending notification email exception "");
        }*/
        sysUserMapper.add(sysUser);
        return true;
    }

    public SysUserMapper getSysUserMapper() {
        return sysUserMapper;
    }

    public void setSysUserMapper(SysUserMapper sysUserMapper) {
        this.sysUserMapper = sysUserMapper;
    }

}

Test transaction

When executing the add test, either the two add operations succeed or fail at the same time. The test code is as follows:

SysUserTest.java executes the testAddUser() method

package com.bdqn;

import com.bdqn.pojo.SysUser;
import com.bdqn.service.SysUserService;
import org.apache.log4j.Logger;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * SysUser Business test
 *
 * @author Miku
 * @since 2021-05-14
 */
public class SysUserTest {

    private Logger logger = Logger.getLogger(SysUserTest.class);

    @Before
    public void setUp() throws Exception {
    }

    /**
     * Test get user list
     */
    @Test
    public void testGetUserList() {
        // Multiple configuration files can be loaded at the same time; other configurations can also be imported in the main configuration file applicationContext.xml (recommended)
        // String[] confs = {"applicationContext.xml","applicationContext-dao.xml","applicationContext-service.xml"};
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        SysUserService userService = (SysUserService) ctx.getBean("sysUserService");
        List<SysUser> userList = new ArrayList<SysUser>();
        SysUser sysUser = new SysUser();
        sysUser.setRealName("Zhao");
        sysUser.setRoleId(2);
        userList = userService.getList(sysUser);

        for (SysUser userResult : userList) {
            logger.debug("testGetUserList account: "
                    + userResult.getAccount() + " and realName: "
                    + userResult.getRealName() + " and roleId: "
                    + userResult.getRoleId() + " and roleName: "
                    + userResult.getRoleIdName() + " and address: "
                    + userResult.getAddress());
        }
    }

    /**
     * Test save user
     */
    @Test
    public void testAddUser() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        SysUserService userService = (SysUserService) ctx.getBean("sysUserService");
        SysUser user = new SysUser();
        user.setAccount("test001");
        user.setRealName("Test user 001");
        user.setPassword("1234567");
        Date birthday = null;
        try {
            birthday = new SimpleDateFormat("yyyy-MM-dd").parse("2021-05-17");
        } catch (ParseException e) {
            e.printStackTrace();
        }
        user.setBirthday(birthday);
        user.setAddress("Address test");
        user.setSex(1);
        user.setPhone("13688783697");
        user.setRoleId(1);
        user.setCreatedUserId(1);
        user.setCreatedTime(new Date());
        boolean isok = userService.add(user);

        logger.debug("testAdd result: " + isok);
    }

}

Execute the test case, view the data in the database, and find that the transaction works.

Tags: Java Maven Spring

Posted on Sat, 30 Oct 2021 08:17:27 -0400 by Woad.php