08.MyBatis源码分析笔记

编写测试代码

Posted by Chen Xingxu on September 15, 2020

08.MyBatis源码分析笔记–编写测试代码

MyBatis 配置

mybatis-config.xml

位置:mybatis-3\src\main\resources\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>

    <properties resource="config.properties">
    </properties>

    <typeAliases>
        <typeAlias type="org.apache.ibatis.demo.pojo.User" alias="user"></typeAlias>
    </typeAliases>
    <plugins>
        <!--监控 sql 埋点 分页-->
        <plugin interceptor="org.apache.ibatis.demo.plugin.SqlPrintInterceptor"></plugin>
    </plugins>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
         <mapper resource="mapper/UserMapper.xml"/>
    </mappers>
</configuration>

config.properties

位置:mybatis-3\src\main\resources\config.properties

driver=com.mysql.cj.jdbc.Driver
#url=jdbc:mysql://127.0.0.1:3306/mybatis_learn?characterEncoding=utf8&amp;serverTimezone=UTC&amp;useSSL=false
url=jdbc:mysql://127.0.0.1:3306/mybatis_learn?characterEncoding=utf8&serverTimezone=UTC&useSSL=false
#url=jdbc:mysql://127.0.0.1:3306/mybatis_learn
username=root
password=root123456

MyBatis Plugin

SqlPrintInterceptor.java

位置:mybatis-3\src\test\java\org\apache\ibatis\demo\plugin\SqlPrintInterceptor.java

package org.apache.ibatis.demo.plugin;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.regex.Matcher;

@Intercepts
        ({
                @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
                @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
        })
public class SqlPrintInterceptor implements Interceptor {
    private static final Logger log = LoggerFactory.getLogger(SqlPrintInterceptor.class);

    private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        Object parameterObject = null;
        if (invocation.getArgs().length > 1) {
            parameterObject = invocation.getArgs()[1];
        }

        long start = System.currentTimeMillis();

        Object result = invocation.proceed();

        String statementId = mappedStatement.getId();
        BoundSql boundSql = mappedStatement.getBoundSql(parameterObject);
        Configuration configuration = mappedStatement.getConfiguration();
        String sql = getSql(boundSql, parameterObject, configuration);

        long end = System.currentTimeMillis();
        long timing = end - start;
        if(log.isInfoEnabled()){
            log.info("执行sql耗时:" + timing + " ms" + " - id:" + statementId + " - Sql:" );
            log.info("   "+sql);
        }

        return result;
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof Executor) {
            return Plugin.wrap(target, this);
        }
        return target;
    }

    @Override
    public void setProperties(Properties properties) {
    }

    private String getSql(BoundSql boundSql, Object parameterObject, Configuration configuration) {
        String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
        if (parameterMappings != null) {
            for (int i = 0; i < parameterMappings.size(); i++) {
                ParameterMapping parameterMapping = parameterMappings.get(i);
                if (parameterMapping.getMode() != ParameterMode.OUT) {
                    Object value;
                    String propertyName = parameterMapping.getProperty();
                    if (boundSql.hasAdditionalParameter(propertyName)) {
                        value = boundSql.getAdditionalParameter(propertyName);
                    } else if (parameterObject == null) {
                        value = null;
                    } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                        value = parameterObject;
                    } else {
                        MetaObject metaObject = configuration.newMetaObject(parameterObject);
                        value = metaObject.getValue(propertyName);
                    }
                    sql = replacePlaceholder(sql, value);
                }
            }
        }
        return sql;
    }

    private String replacePlaceholder(String sql, Object propertyValue) {
        String result;
        if (propertyValue != null) {
            if (propertyValue instanceof String) {
                result = "'" + propertyValue + "'";
            } else if (propertyValue instanceof Date) {
                result = "'" + DATE_FORMAT.format(propertyValue) + "'";
            } else {
                result = propertyValue.toString();
            }
        } else {
            result = "null";
        }
        return sql.replaceFirst("\\?", Matcher.quoteReplacement(result));
    }
}

业务类

建表 SQL

CREATE TABLE `mybatis_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `username` varchar(255) DEFAULT NULL COMMENT '姓名',
  `age` int(11) DEFAULT NULL COMMENT '年龄',
  `phone` bigint(20) DEFAULT NULL COMMENT '电话号码',
  `description` varchar(255) DEFAULT NULL COMMENT '描述',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8mb4;

entities

User.java

位置:mybatis-3\src\test\java\org\apache\ibatis\demo\pojo\User.java

package org.apache.ibatis.demo.pojo;

import java.io.Serializable;

public class User implements Serializable {
    private Long id;

    private String username;

    private Integer age;

    private Long phone;

    private String description;

    //Getter和Setter方法略
    //toString方法略
}

DAO 层

UserMapper.java

位置:mybatis-3\src\test\java\org\apache\ibatis\demo\mapper\UserMapper.java

package org.apache.ibatis.demo.mapper;


import org.apache.ibatis.demo.pojo.User;

public interface UserMapper {
    User selectUser(Integer id);
}

UserMapper.xml

位置:mybatis-3\src\main\resources\mapper\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="org.apache.ibatis.demo.mapper.UserMapper">
  <resultMap id="BaseResultMap" type="user">

    <id column="id" jdbcType="BIGINT" property="id" />
    <result column="username" jdbcType="VARCHAR" property="username" />
    <result column="age" jdbcType="INTEGER" property="age" />
    <result column="phone" jdbcType="BIGINT" property="phone" />
    <result column="description" jdbcType="VARCHAR" property="description" />
  </resultMap>
    <select id="selectUser" parameterType="integer" resultType="user" resultMap="BaseResultMap">
    select * from mybatis_user where id = #{id}
  </select>
</mapper>

测试类

MybatisMain.java

位置:mybatis-3\src\test\java\org\apache\ibatis\demo\MybatisMain.java

package org.apache.ibatis.demo;

import org.apache.ibatis.demo.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

public class MybatisMain {
  public static void main(String[] args) throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    //mapper namespace 和 select id
    User user = sqlSession.selectOne("org.apache.ibatis.demo.mapper.UserMapper.selectUser", 11);
    System.out.println(user);
  }
}