SpringBoot tutorial | SpringBoot integration with Mybatis

In the last article, we introduced the integration of SpringBoot with JdbcTemplate After a brief experience of the usage of the JdbcTemplate framework, today's content is more important. Let's introduce the steps of integrating Mybatis with SpringBoot.

1. Introduction to Mybatis

MyBatis was originally an open source project iBATIS of apache. In 2010, the project was migrated from apache software foundation to google code and renamed MyBatis. iBATIS comes from the combination of "internet" and "abatis". It is a persistence layer framework based on Java. The persistence layer framework provided by iBATIS includes SQL Maps and Data Access Objects (DAO).

MyBatis is an excellent persistence layer framework that supports customized SQL, stored procedures and advanced mapping. MyBatis avoids almost all JDBC code and manually setting parameters and extracting result sets. MyBatis uses simple XML or annotations to configure and map primitives, and maps interfaces and Java POJOs(Plain Old Java Objects) into records in the database.

Mybatis features:

1. Mybatis implements interface binding, which is more convenient to use.

2. The improvement of object relationship mapping is more efficient

3. MyBatis uses powerful OGNL based expressions to eliminate other elements.

advantage:

1. Easy to learn

mybatis itself is small and simple. There is no third-party dependency. The simplest installation is as long as two jar files + several sql mapping files are configured. It is easy to learn and use. Through documents and source code, you can fully master its design idea and implementation.

2. Flexible

mybatis does not impose any impact on the existing design of the application or database. sql is written in xml to facilitate unified management and optimization. Through sql, we can basically realize all the functions we can achieve without using the data access framework, perhaps more.

3. Decouple sql from program code

By providing DAL layer, the business logic and data access logic are separated, which makes the design of the system clearer, easier to maintain and easier to unit test. The separation of sql and code improves maintainability.

4. Provide mapping labels to support the mapping between objects and orm fields in the database

5. Provide object relationship mapping labels to support the establishment and maintenance of object relationships

6. Provide xml tags to support writing dynamic sql.

Disadvantages:

1. There is a lot of work when writing SQL statements, especially when there are many fields and associated tables.

2. SQL statements depend on the database, resulting in poor portability of the database, and the database cannot be replaced.

3. The framework is still relatively simple, and the functions are still missing. Although the data binding code is simplified, the whole underlying database query actually needs to be written by itself, the workload is relatively large, and it is not easy to adapt to rapid database modification.

4. Poor L2 cache mechanism

The above contents are copied from the Internet, because I think most people should have used Mybatis, and the focus of this article is mainly on the integration of SpringBoot, rather than introducing Mybatis from the beginning. If you don't know much about the use of Mybatis, it is recommended to learn the usage of Mybatis first.

2. Integration steps

Next, let's start the integration. In order to facilitate operation, since we just integrated the JdbcTemplate last time, there may be problems with multiple DAO layer frameworks in the code. We pulled a new branch based on the original project for development, and the branch name is feature/mybaits. The completed code will be hosted on gitCode. You can get the address at the end of the text.

2.1 introducing dependencies

First of all, we need to introduce the dependencies required by mybatis. Mybatis itself has provided a Starter for adapting to springBoot. At the same time, we also need to introduce MySQL connector In POM Add to XML:

<!-- lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.22</version>
    <scope>provided</scope>
</dependency>

<!-- mybatis-spring-boot-starter -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.1</version>
</dependency>

<!-- MySQL connect -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

2.2 configuring database connections

In the spring configuration file, configure the connection information of the database we need to access. This configuration is the same as the previous configuration of JDBC template

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springboot_learning
    username: root
    password: root

2.3 Mapper development

In the JdbcTemplate, the layer where we access the database is represented by dao. However, in mybatis, we generally call this layer mapper, and the general class name also ends with this. In fact, it represents the problem of usage habits. Mapper in mybatis is also divided into interface and implementation. In particular, the implementation of mapper is generally embodied in the form of xml file. Our sql is also written in xml files.

We create a mapper folder in the project to store all mapper interfaces. We create a UserMapper inside to handle the addition, deletion, modification and query of the user table.

@Mapper
public interface UserMapper {
    /**
     * Delete operation
     * @param id
     * @return
     */
    int deleteByPrimaryKey(Integer id);

    /**
     * Insert operation
     * @param record
     * @return
     */
    int insert(User record);

    /**
     * Plug in operation
     * @param record
     * @return
     */
    int insertSelective(User record);

    /**
     * Query by id
     * @param id
     * @return
     */
    User selectByPrimaryKey(Integer id);

    /**
     * update operation
     * @param record
     * @return
     */
    int updateByPrimaryKeySelective(User record);

    /**
     * update operation
     * @param record
     * @return
     */
    int updateByPrimaryKey(User record);
}

Then create a mapper folder in the resources directory (application.yml directory at the same level) to store the mapper implementation in XML format. Write a usermapper xml

<?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.lsqingfeng.springboot.mapper.UserMapper">

    <resultMap id="BaseResultMap" type="com.lsqingfeng.springboot.entity.User">
        <id column="id" jdbcType="INTEGER" property="id" />
        <result column="name" jdbcType="VARCHAR" property="name" />
        <result column="age" jdbcType="INTEGER" property="age" />
        <result column="address" jdbcType="VARCHAR" property="address" />
        <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
        <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
    </resultMap>


    <!--sql Statement fragment, extract the common part-->
    <sql id="Base_Column_List">
        id, name, age,address,create_time, update_time
    </sql>

    <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List" />
        from t_user
        where id = #{id,jdbcType=INTEGER}
    </select>

    <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
        delete from t_user
        where id = #{id,jdbcType=INTEGER}
    </delete>

    <insert id="insert" parameterType="com.lsqingfeng.springboot.entity.User">
        insert into t_user ( name, age,address,create_time ,update_time
        )
        values (#{name,jdbcType=VARCHAR}, #{age,jdbcType=INTEGER},#{address},#{createTime,jdbcType=TIMESTAMP},#{updateTime,jdbcType=TIMESTAMP}
               )
    </insert>


    <!--dynamic sql-->
    <insert id="insertSelective" parameterType="com.lsqingfeng.springboot.entity.User">
        insert into t_user
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="id != null">
                id,
            </if>
            <if test="name != null">
                name,
            </if>
            <if test="age != null">
                age,
            </if>
            <if test="address != null">
                address,
            </if>
            <if test="createTime != null">
                create_time,
            </if>
            <if test="updateTime != null">
                update_time,
            </if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="id != null">
                #{id,jdbcType=INTEGER},
            </if>
            <if test="name != null">
                #{name,jdbcType=VARCHAR},
            </if>
            <if test="age != null">
                #{age,jdbcType=INTEGER},
            </if>
            <if test="address != null">
                #{address},
            </if>
            <if test="updateTime != null">
                #{createTime,jdbcType=TIMESTAMP},
            </if>
            <if test="updateTime != null">
                #{updateTime,jdbcType=TIMESTAMP},
            </if>
        </trim>
    </insert>


    <update id="updateByPrimaryKeySelective" parameterType="com.lsqingfeng.springboot.entity.User">
        update t_user
        <set>
            <if test="name != null">
                name = #{name,jdbcType=VARCHAR},
            </if>
            <if test="age != null">
                age = #{age,jdbcType=INTEGER},
            </if>
            <if test="address != null">
                address = #{address},
            </if>
            <if test="createTime != null">
                create_time = #{createTime,jdbcType=TIMESTAMP},
            </if>
            <if test="age != null">
                update_time = #{updateTime,jdbcType=TIMESTAMP}
            </if>
        </set>
        where id = #{id,jdbcType=INTEGER}
    </update>

    <update id="updateByPrimaryKey" parameterType="com.lsqingfeng.springboot.entity.User">
        update t_user
        set name = #{name,jdbcType=VARCHAR},
            age = #{age,jdbcType=INTEGER},
            address = #{address,jdbcType=VARCHAR},
            create_time = #{createTime,jdbcType=TIMESTAMP},
            update_time = #{updateTime,jdbcType=TIMESTAMP}
        where id = #{id,jdbcType=INTEGER}
    </update>
</mapper>

Then refer to mapper.com in the Service layer

2.4 configuring Mapper paths

Now we put all the xml files in the mapper folder under resources. But this path is unknown to mybatis in our project, so we need to tell it the location of this path. How to tell? It's in application Configure it in YML.

mybatis:
  mapper-locations: classpath:mapper/*.xml

The mapper is located in all xml terminated files in the mapper folder under the classpath.

Here is another question. The location of Mapper's interface is not configured. How does he know? In fact, we have added @ Mapper annotation to each Mapper's interface, so he can automatically scan it. If you want to add this annotation, you need to add it on all Mapper interfaces, which is actually troublesome. What to do? We can not add this annotation, and then add a MapperScan annotation on the startup class of SpringBoot, and configure the package path of Mapper, so we don't need to add it.

If we remove the @ Mapper annotation and do not add any configuration, an error will be reported:

We add comments on the main class:

@SpringBootApplication
@MapperScan("com.lsqingfeng.springboot.mapper")
public class SpringBootLearningApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootLearningApplication.class, args);
    }
}

Restart:

succeed.

3. Interface test

After assembling the project, let's call the interface. Continue to use the Controller written in jdbc for testing.

After the call is successful, observe the database.

Li Si makes what we just inserted, but the two times are null, because we don't assign a value to the time.

Look at the query interface.

4. mybatis auto fill time

In the above case, because we did not set the time, the creation time and modification time are empty. In fact, Mybatis provides us with an interceptor mechanism, which is equivalent to intercepting the sql executed each time, so that we can judge the operation. If it is an insert operation, we can directly set the creation time and modification time as the current time. If it is an update operation, set the update time to the current time.

The interceptor code is as follows.

import org.apache.ibatis.binding.MapperMethod.ParamMap;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.Date;

/**
 * Mybatis Interceptor, which can be used to set the creation time and update time
 */
@Component
@Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) })
public class CreateTimeSetInterceptor implements Interceptor {

  private static final String CREATE_TIME_SETTER = "setCreateTime";
  private static final String UPDATE_TIME_SETTER = "setUpdateTime";

  @Override
  public Object intercept(Invocation invocation) throws Throwable {
    MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
    Object parameter = invocation.getArgs()[1];

    if (ms.getSqlCommandType() == SqlCommandType.INSERT) {
      setTimeIfNeccesary(parameter, CREATE_TIME_SETTER);
      setTimeIfNeccesary(parameter, UPDATE_TIME_SETTER);
    } else if (ms.getSqlCommandType() == SqlCommandType.UPDATE) {
      setTimeIfNeccesary(parameter, UPDATE_TIME_SETTER);
    }
    return invocation.getMethod().invoke(invocation.getTarget(), invocation.getArgs());
  }

  private void setTimeIfNeccesary(Object param, String methodName) {
    Class<?> cls = param.getClass();

    if (cls == ParamMap.class) {
      @SuppressWarnings("unchecked")
      ParamMap<Object> map = (ParamMap<Object>) param;
      map.entrySet().forEach(entry -> {
        if (!entry.getKey().equals("et")) {
          setIfSetterExist(entry.getValue(), methodName);
        }
      });
    } else {
      setIfSetterExist(param, methodName);
    }
  }

  private void setIfSetterExist(Object param, String methodName) {
    Class<?> cls = param.getClass();
    try {
      Method m = null;
      try {
        m = cls.getDeclaredMethod(methodName, new Class[] { Date.class });
        if (m != null) {
          m.setAccessible(true);
          m.invoke(param, new Date());
        }
      } catch (NoSuchMethodException e1) {
        m = cls.getDeclaredMethod(methodName, new Class[] { Timestamp.class });
        if (m != null) {
          m.setAccessible(true);
          m.invoke(param, new Timestamp(System.currentTimeMillis()));
        }
      }
    } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException
        | InvocationTargetException e) {
      e.printStackTrace();
    }
  }

}

Next, let's perform an insert operation

Observe the database again:

Find that time is already worth it.

5. Mybatis generator plug-in

We created the UserMapper interface and the xml implementation of UserMapper through the User table above. In fact, the contents of such files are relatively regular, so it is time-consuming if you don't write them yourself every time. All mybatis provides us with mybatis generator plug-in. Through this plug-in, we can set the corresponding database connection and table name, and then the plug-in will help us automatically generate the corresponding entity, mapper interface and mapper implementation, There are only common operations of adding, deleting, modifying and querying. It can greatly reduce the workload of our operation. This step is generally called mybatis reverse generation. There are many methods of reverse generation. If you are interested, go find it yourself. I don't want to expand it here, because now with the popularity of mybatisPlus, there are more uses of mybatis plus generator.

Well, that's all we have to say about the integration of spring boot with Mybatis. Welcome to communicate with us. If you have any questions, please leave a message at any time.

In addition: the supporting project code has been managed. gitCode: gitcode.net/lsqingfeng/...

All articles will be updated in WeChat official account. Welcome to the attention: a breeze for 82 years.

Keywords: Java Spring Spring Boot

Added by dieselmachine on Wed, 26 Jan 2022 06:55:28 +0200