MyBatis学习

一、Mybatis使用
1、新建mybatis配置文件

<?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="jdbc.properties">
    </properties>
    <!-- 配置setting属性-->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <!-- 开启了一个驼峰命名规则-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

    <typeAliases>
        <package name="cn.j3code.studyspring.mybatis.helloworld.entity"/>
    </typeAliases>
    <!-- 配置数据库-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <!-- 配置连接池 -->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

    <!--  mappers中注册我们所有写的dao接口的实现(映射)文件-->
    <mappers>
        <mapper resource="cn/j3code/studyspring/mybatis/helloworld/mapper/UserMapper.xml"/>
        <!-- 如果映射文件有十几百个的话,可以用下面的全局注册
        	<package name="文件所在包路径"></package>
			<package name="cn.liuliang.Dao"></package>
		-->
    </mappers>
</configuration>

2、新建UserMapper.xml用于测试

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="cn.j3code.studyspring.mybatis.helloworld.mapper.UserMapper">
    <select id="findAll" resultType="cn.j3code.studyspring.mybatis.helloworld.entity.User">
        select * from t_user;
    </select>


    <resultMap id="user" type="cn.j3code.studyspring.mybatis.helloworld.entity.User">
        <result property="name" javaType="string" column="name" jdbcType="VARCHAR"/>
    </resultMap>
    <select id="findById" parameterType="int" resultMap="user">
        select * from t_user where id=#{id}
    </select>

    <insert id="insert" parameterType="user">
        insert into t_user(id, name, age) values(#{id},#{name},#{age})
    </insert>

</mapper>

3、实际使用样例

package cn.j3code.studyspring.mybatis;

import cn.j3code.studyspring.mybatis.helloworld.entity.User;
import cn.j3code.studyspring.mybatis.helloworld.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.core.io.ClassPathResource;

import java.io.IOException;

@Slf4j
public class MyBatisTest {
    private SqlSession sqlSession;

    @BeforeEach
    public void startUp() throws IOException{
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        ClassPathResource resource = new ClassPathResource("myBatisConfig.xml");
        SqlSessionFactory sqlSessionFactory = builder.build(resource.getInputStream());
        sqlSession = sqlSessionFactory.openSession();
    }

    @Test
    public void testMybatis() {
        try {
            // 第一个参数为xml文件的namespace+方法名
            int rows = sqlSession.insert("cn.j3code.studyspring.mybatis.helloworld.mapper.UserMapper.insert"
                    , new User(null, "JERRY", 18));
            sqlSession.commit();
            log.info("rows -> {}", rows);
        }catch (Exception e){
            e.printStackTrace();
            sqlSession.rollback();
        }finally {
            sqlSession.close();
        }
    }

    @Test
    public void testProcy() throws IOException{
        try {
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            int rows = mapper.insert(new User(null, "JERRY", 19));
            sqlSession.commit();
            log.info("rows -> {}", rows);
        }catch (Exception e){
            e.printStackTrace();
            sqlSession.rollback();
        }finally {
            sqlSession.close();
        }
    }

}

二、常用类
1、XpathParser
Xpath可以简单的使用路径表达式在XML文档中选取节点元素。该工具核心作用就是解析xml文件,包括我们的配置文件和mapper文件。这项技术也是爬虫核心技术之一。在浏览器中我们可以轻松的获取一个标签的xpath。
在这里插入图片描述
粘贴到的路径如下:
/html/body/div[1]/div[1]/div[2]/div/div[2]/div[3]
含义:根目录下的html下的body下的第一个div下的第一个div下的第二个div下的第一个div下的第二个div下的第三个div。
测试demo

	@Test
    public void testxPathParser() throws Exception{
        ClassPathResource resource = new ClassPathResource("myBatisConfig.xml");
        XPathParser xPathParser = new XPathParser(resource.getInputStream());
        XNode xNode = xPathParser.evalNode("/configuration/properties");

        log.info("node -> {}", xNode);
    }

2、Configuration类
a.测试组装Configuration

	@Test
    public void testConfiguration() throws Exception{
        Configuration configuration = new Configuration();
        Properties properties = new Properties();
        properties.put("driver", "com.mysql.cj.jdbc.Driver");
        properties.put("url", "jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&useSSL=false&characterEncoding=utf8&allowMultiQueries=true&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true");
        properties.put("username", "root");
        properties.put("password", "root");
        configuration.setVariables(properties);
        // 添加日志实现和驼峰式命名
        configuration.setLogImpl(Slf4jImpl.class);
        configuration.setMapUnderscoreToCamelCase(true);
        // 配置别名
        configuration.getTypeAliasRegistry().registerAliases("cn.j3code.studyspring.mybatis.helloworld.entity");
        // 配置环境
        PooledDataSource pooledDataSource = new PooledDataSource();
        pooledDataSource.setDriver(configuration.getVariables().getProperty("driver"));
        pooledDataSource.setUrl(configuration.getVariables().getProperty("url"));
        pooledDataSource.setUsername(configuration.getVariables().getProperty("username"));
        pooledDataSource.setPassword(configuration.getVariables().getProperty("password"));
        JdbcTransactionFactory jdbcTransactionFactory = new JdbcTransactionFactory();
        Environment environment = new Environment("development", jdbcTransactionFactory, pooledDataSource);
        configuration.setEnvironment(environment);

        StaticSqlSource sqlSource = new StaticSqlSource(configuration,
                "insert into user(name, age) values ('jerry', 1000)");
        MappedStatement mappedStatement = new MappedStatement.Builder(
                configuration,
                "study.insert",
                sqlSource,
                SqlCommandType.INSERT
        ).build();
        // 扫描文件也是将xml中每个标签作为一个statement添加到configuration中
        configuration.addMappedStatement(mappedStatement);
        log.info("configuration -> {}", configuration);

    }

b. 使用XMLConfigBuilder接下生成Configuration

 	@Test
    public void testXMLConfigBuilder() throws Exception{
        ClassPathResource resource = new ClassPathResource("myBatisConfig.xml");
        XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder(resource.getInputStream());
        Configuration configuration = xmlConfigBuilder.parse();
        log.info("configuration -> {}", configuration);

    }
 
	 // 进入parse方法
	 private final XPathParser parser;
	 public Configuration parse() {
	        if (this.parsed) {
	            throw new BuilderException("Each XMLConfigBuilder can only be used once.");
	        } else {
	        	// 解析标志位 
	            this.parsed = true;
	            // 使用XPathParser获取configuration节点并调用parseConfiguration逐个解析
	            this.parseConfiguration(this.parser.evalNode("/configuration"));
	            return this.configuration;
	        }
	    }
    
    // 逐个获取解析节点
	private void parseConfiguration(XNode root) {
	        try {
	            this.propertiesElement(root.evalNode("properties"));
	            Properties settings = this.settingsAsProperties(root.evalNode("settings"));
	            this.loadCustomVfs(settings);
	            this.typeAliasesElement(root.evalNode("typeAliases"));
	            this.pluginElement(root.evalNode("plugins"));
	            this.objectFactoryElement(root.evalNode("objectFactory"));
	            this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
	            this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
	            this.settingsElement(settings);
	            this.environmentsElement(root.evalNode("environments"));
	            this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
	            this.typeHandlerElement(root.evalNode("typeHandlers"));
	            this.mapperElement(root.evalNode("mappers"));
	        } catch (Exception var3) {
	            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
	        }
	    }

3、OGNL表达式

 	@Test
    public void testOGNL() throws OgnlException {
        User user = new User(1, "tom", 200);
        Object name = Ognl.getValue("name != null && name == 'hh'", user);
        log.info("name -> {}", name);
    }

4、别名注册器
mybatis提供了TypeAliasRegistry作为别名注册器,同时默认注入了大量的基础类型的别名,他是配置类的一个成员变量:

protected final TypeAliasRegistry typeAliasRegistry;

this.typeAliasRegistry = new TypeAliasRegistry();


// 实现如下
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.apache.ibatis.type;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.apache.ibatis.io.ResolverUtil;
import org.apache.ibatis.io.Resources;

public class TypeAliasRegistry {

	// key:别名, value: class
    private final Map<String, Class<?>> TYPE_ALIASES = new HashMap();

    public TypeAliasRegistry() {
        this.registerAlias("string", String.class);
        this.registerAlias("byte", Byte.class);
        this.registerAlias("long", Long.class);
        this.registerAlias("short", Short.class);
        this.registerAlias("int", Integer.class);
        this.registerAlias("integer", Integer.class);
        this.registerAlias("double", Double.class);
        this.registerAlias("float", Float.class);
        this.registerAlias("boolean", Boolean.class);
        this.registerAlias("byte[]", Byte[].class);
        this.registerAlias("long[]", Long[].class);
        this.registerAlias("short[]", Short[].class);
        this.registerAlias("int[]", Integer[].class);
        this.registerAlias("integer[]", Integer[].class);
        this.registerAlias("double[]", Double[].class);
        this.registerAlias("float[]", Float[].class);
        this.registerAlias("boolean[]", Boolean[].class);
        this.registerAlias("_byte", Byte.TYPE);
        this.registerAlias("_long", Long.TYPE);
        this.registerAlias("_short", Short.TYPE);
        this.registerAlias("_int", Integer.TYPE);
        this.registerAlias("_integer", Integer.TYPE);
        this.registerAlias("_double", Double.TYPE);
        this.registerAlias("_float", Float.TYPE);
        this.registerAlias("_boolean", Boolean.TYPE);
        this.registerAlias("_byte[]", byte[].class);
        this.registerAlias("_long[]", long[].class);
        this.registerAlias("_short[]", short[].class);
        this.registerAlias("_int[]", int[].class);
        this.registerAlias("_integer[]", int[].class);
        this.registerAlias("_double[]", double[].class);
        this.registerAlias("_float[]", float[].class);
        this.registerAlias("_boolean[]", boolean[].class);
        this.registerAlias("date", Date.class);
        this.registerAlias("decimal", BigDecimal.class);
        this.registerAlias("bigdecimal", BigDecimal.class);
        this.registerAlias("biginteger", BigInteger.class);
        this.registerAlias("object", Object.class);
        this.registerAlias("date[]", Date[].class);
        this.registerAlias("decimal[]", BigDecimal[].class);
        this.registerAlias("bigdecimal[]", BigDecimal[].class);
        this.registerAlias("biginteger[]", BigInteger[].class);
        this.registerAlias("object[]", Object[].class);
        this.registerAlias("map", Map.class);
        this.registerAlias("hashmap", HashMap.class);
        this.registerAlias("list", List.class);
        this.registerAlias("arraylist", ArrayList.class);
        this.registerAlias("collection", Collection.class);
        this.registerAlias("iterator", Iterator.class);
        this.registerAlias("ResultSet", ResultSet.class);
    }

    public <T> Class<T> resolveAlias(String string) {
        try {
            if (string == null) {
                return null;
            } else {
                String key = string.toLowerCase(Locale.ENGLISH);
                Class value;
                if (this.TYPE_ALIASES.containsKey(key)) {
                    value = (Class)this.TYPE_ALIASES.get(key);
                } else {
                    value = Resources.classForName(string);
                }

                return value;
            }
        } catch (ClassNotFoundException var4) {
            throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + var4, var4);
        }
    }

    public void registerAliases(String packageName) {
        this.registerAliases(packageName, Object.class);
    }

    public void registerAliases(String packageName, Class<?> superType) {
        ResolverUtil<Class<?>> resolverUtil = new ResolverUtil();
        resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
        Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
        Iterator var5 = typeSet.iterator();

        while(var5.hasNext()) {
            Class<?> type = (Class)var5.next();
            if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
                this.registerAlias(type);
            }
        }

    }

    public void registerAlias(Class<?> type) {
        String alias = type.getSimpleName();
        Alias aliasAnnotation = (Alias)type.getAnnotation(Alias.class);
        if (aliasAnnotation != null) {
            alias = aliasAnnotation.value();
        }

        this.registerAlias(alias, type);
    }

    public void registerAlias(String alias, Class<?> value) {
        if (alias == null) {
            throw new TypeException("The parameter alias cannot be null");
        } else {
            String key = alias.toLowerCase(Locale.ENGLISH);
            if (this.TYPE_ALIASES.containsKey(key) && this.TYPE_ALIASES.get(key) != null && !((Class)this.TYPE_ALIASES.get(key)).equals(value)) {
                throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + ((Class)this.TYPE_ALIASES.get(key)).getName() + "'.");
            } else {
                this.TYPE_ALIASES.put(key, value);
            }
        }
    }

    public void registerAlias(String alias, String value) {
        try {
            this.registerAlias(alias, Resources.classForName(value));
        } catch (ClassNotFoundException var4) {
            throw new TypeException("Error registering type alias " + alias + " for " + value + ". Cause: " + var4, var4);
        }
    }

    public Map<String, Class<?>> getTypeAliases() {
        return Collections.unmodifiableMap(this.TYPE_ALIASES);
    }
}

测试注册别名

	@Test
    public void testTypeAliasRegistry(){
        TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
        // 提供两种方法
        // 1.注册单个别名
        typeAliasRegistry.registerAlias("user", User.class);
        // 2.使用包扫描的方式注册别名
        typeAliasRegistry.registerAliases("com.study.entity");
    }
	
	// 注册单个别名方法
	 public void registerAlias(String alias, Class<?> value) {
        if (alias == null) {
            throw new TypeException("The parameter alias cannot be null");
        } else {
        	// 先将别名转为小写 
            String key = alias.toLowerCase(Locale.ENGLISH);
            // 判断别名是否已经存在
            if (this.TYPE_ALIASES.containsKey(key) && this.TYPE_ALIASES.get(key) != null && !((Class)this.TYPE_ALIASES.get(key)).equals(value)) {
                throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + ((Class)this.TYPE_ALIASES.get(key)).getName() + "'.");
            } else {
            	// 不存在,则放入map
                this.TYPE_ALIASES.put(key, value);
            }
        }
    }

	// 注册批量别名方法
	public void registerAliases(String packageName) {
        this.registerAliases(packageName, Object.class);
    }

    public void registerAliases(String packageName, Class<?> superType) {
        ResolverUtil<Class<?>> resolverUtil = new ResolverUtil();
        resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
        // 获取传入包下面的所有类
        Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
        Iterator var5 = typeSet.iterator();

        while(var5.hasNext()) {
            Class<?> type = (Class)var5.next();
            // 不是匿名类 | 不是接口 | 不是内部类
            if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
                this.registerAlias(type);
            }
        }

    }

	public void registerAlias(Class<?> type) {
		// 获取类型名称 
        String alias = type.getSimpleName();
        // 类上有Alias注解的话获取Alias注解中配置的名称替换之
        Alias aliasAnnotation = (Alias)type.getAnnotation(Alias.class);
        if (aliasAnnotation != null) {
            alias = aliasAnnotation.value();
        }
		// 调用单个别名注册方法注册之
        this.registerAlias(alias, type);
    }

另外在Configuration初始化时,也会自动注册大量别名

public Configuration() {
        this.safeResultHandlerEnabled = true;
        this.multipleResultSetsEnabled = true;
        this.useColumnLabel = true;
        this.cacheEnabled = true;
        this.useActualParamName = true;
        this.localCacheScope = LocalCacheScope.SESSION;
        this.jdbcTypeForNull = JdbcType.OTHER;
        this.lazyLoadTriggerMethods = new HashSet(Arrays.asList("equals", "clone", "hashCode", "toString"));
        this.defaultExecutorType = ExecutorType.SIMPLE;
        this.autoMappingBehavior = AutoMappingBehavior.PARTIAL;
        this.autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;
        this.variables = new Properties();
        this.reflectorFactory = new DefaultReflectorFactory();
        this.objectFactory = new DefaultObjectFactory();
        this.objectWrapperFactory = new DefaultObjectWrapperFactory();
        this.lazyLoadingEnabled = false;
        this.proxyFactory = new JavassistProxyFactory();
        this.mapperRegistry = new MapperRegistry(this);
        this.interceptorChain = new InterceptorChain();
        this.typeHandlerRegistry = new TypeHandlerRegistry();
        this.typeAliasRegistry = new TypeAliasRegistry();
        this.languageRegistry = new LanguageDriverRegistry();
        this.mappedStatements = new StrictMap("Mapped Statements collection");
        this.caches = new StrictMap("Caches collection");
        this.resultMaps = new StrictMap("Result Maps collection");
        this.parameterMaps = new StrictMap("Parameter Maps collection");
        this.keyGenerators = new StrictMap("Key Generators collection");
        this.loadedResources = new HashSet();
        this.sqlFragments = new StrictMap("XML fragments parsed from previous mappers");
        this.incompleteStatements = new LinkedList();
        this.incompleteCacheRefs = new LinkedList();
        this.incompleteResultMaps = new LinkedList();
        this.incompleteMethods = new LinkedList();
        this.cacheRefMap = new HashMap();
		
		// 注册别名
        this.typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
        this.typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
        this.typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
        this.typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
        this.typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
        this.typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
        this.typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
        this.typeAliasRegistry.registerAlias("LRU", LruCache.class);
        this.typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
        this.typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
        this.typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
        this.typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
        this.typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
        this.typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
        this.typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
        this.typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
        this.typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
        this.typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
        this.typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
        this.typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
        this.typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
        this.typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

        this.languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
        this.languageRegistry.register(RawLanguageDriver.class);
    }

5、类型转换器
5.1 TypeHandler
TypeHandler负责对查询结果进行类型转换,我们可以使用ResultSet的原生方法对结果进行类型转换。我们当然可以使用switch-case语法通过判断类型选择调用方法如下:

public Object getResult(JDBCType jdbcType, ResultSet rs, String columnName) throws Exception{
        Object object = null;
        
        switch (jdbcType){
            case INTEGER:  
                object = rs.getInt(columnName);
            case BIGINT:
                object = rs.getLong(columnName);
            case VARCHAR:
                object = rs.getString(columnName);
            // 省略其他众多情况
            
        }
        return object;
    }

但这样是面向过程的编码风格,对扩展不利,以后每次新增类型都要修改代码。因此mybatis抽象出一个TypeHandler作为顶层接口,对转换工作做了模型抽离,如下:

public interface TypeHandler<T> {
    void setParameter(PreparedStatement var1, int var2, T var3, JdbcType var4) throws SQLException;

    T getResult(ResultSet var1, String var2) throws SQLException;

    T getResult(ResultSet var1, int var2) throws SQLException;

    T getResult(CallableStatement var1, int var2) throws SQLException;
}

同时实现了大量的实现策略(策略设计模式),核心策略在构造时注册,新增的通过配置文件添加,极大降低了系统耦合性。
在这里插入图片描述
5.2、TypeHandlerRegistry
mybatis提供了一个专门的注册器用来注册TypeHandler。TypeHandlerRegistry也是配置类的一个成员变量:

protected final TypeHandlerRegistry typeHandlerRegistry;

TypeHandlerRegistry的作用如下:

  • 一是为了注册存储类型转换器
  • 二是为了根据javaType或者jdbcType查询合适的TypeHandler

当然,我们可以思考一下javaType–jdbcType以及TypeHandler三者的关系:
事实上,我们一般情况会根据BaseTypeHandler的泛型来确定该处理器的javaType,从而确定使用的是rs的getXXX和setXXX方法,jdbcType本质上没有什么具体的作用,更多的是作为一种标识,用来确定使用一个唯一的handler,在定义TypeHandler时需要指定泛型,也就意味着javaType一定会被指定,那么我们选取TypeHandler时就会有以下两种情况:

  • 1、指定了javaType,也指定了jdbcType,我们可以匹配一个确定的TypeHandler。
  • 2、指定了javaType,未指定jdbcType,我们可以匹配更多TypeHandler,但我们只选一个作为默认

TypeHandlerRegistry源码:

public final class TypeHandlerRegistry {
	// jdbcType与handler映射,jdbcType为一个枚举类
    private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap(JdbcType.class);
    // javaType与map(jdbc-handler map)的映射,一个javaType可能对应多个jdbcType
    private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new ConcurrentHashMap();
    // 兜底方案,当所有typeHandler无法处理时,尝试采用该handler处理
    private final TypeHandler<Object> UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler(this);
    // 所有的类型转换器,
    private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap();
    // 存储只知道jdbcType不知道javaType
    private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = new HashMap();
    
    private Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class;

	// 构造方法
	public TypeHandlerRegistry() {
		// 传入java类型 
        this.register((Class)Boolean.class, (TypeHandler)(new BooleanTypeHandler()));
        this.register((Class)Boolean.TYPE, (TypeHandler)(new BooleanTypeHandler()));
        this.register((JdbcType)JdbcType.BOOLEAN, (TypeHandler)(new BooleanTypeHandler()));
        this.register((JdbcType)JdbcType.BIT, (TypeHandler)(new BooleanTypeHandler()));
        this.register((Class)Byte.class, (TypeHandler)(new ByteTypeHandler()));
        this.register((Class)Byte.TYPE, (TypeHandler)(new ByteTypeHandler()));
        this.register((JdbcType)JdbcType.TINYINT, (TypeHandler)(new ByteTypeHandler()));
        this.register((Class)Short.class, (TypeHandler)(new ShortTypeHandler()));
        this.register((Class)Short.TYPE, (TypeHandler)(new ShortTypeHandler()));
        this.register((JdbcType)JdbcType.SMALLINT, (TypeHandler)(new ShortTypeHandler()));
        this.register((Class)Integer.class, (TypeHandler)(new IntegerTypeHandler()));
        this.register((Class)Integer.TYPE, (TypeHandler)(new IntegerTypeHandler()));
        this.register((JdbcType)JdbcType.INTEGER, (TypeHandler)(new IntegerTypeHandler()));
        this.register((Class)Long.class, (TypeHandler)(new LongTypeHandler()));
        this.register((Class)Long.TYPE, (TypeHandler)(new LongTypeHandler()));
        this.register((Class)Float.class, (TypeHandler)(new FloatTypeHandler()));
        this.register((Class)Float.TYPE, (TypeHandler)(new FloatTypeHandler()));
        this.register((JdbcType)JdbcType.FLOAT, (TypeHandler)(new FloatTypeHandler()));
        this.register((Class)Double.class, (TypeHandler)(new DoubleTypeHandler()));
        this.register((Class)Double.TYPE, (TypeHandler)(new DoubleTypeHandler()));
        this.register((JdbcType)JdbcType.DOUBLE, (TypeHandler)(new DoubleTypeHandler()));
        this.register((Class)Reader.class, (TypeHandler)(new ClobReaderTypeHandler()));
        this.register((Class)String.class, (TypeHandler)(new StringTypeHandler()));
        this.register((Class)String.class, JdbcType.CHAR, (TypeHandler)(new StringTypeHandler()));
        this.register((Class)String.class, JdbcType.CLOB, (TypeHandler)(new ClobTypeHandler()));
        this.register((Class)String.class, JdbcType.VARCHAR, (TypeHandler)(new StringTypeHandler()));
        this.register((Class)String.class, JdbcType.LONGVARCHAR, (TypeHandler)(new ClobTypeHandler()));
        this.register((Class)String.class, JdbcType.NVARCHAR, (TypeHandler)(new NStringTypeHandler()));
        this.register((Class)String.class, JdbcType.NCHAR, (TypeHandler)(new NStringTypeHandler()));
        this.register((Class)String.class, JdbcType.NCLOB, (TypeHandler)(new NClobTypeHandler()));
        this.register((JdbcType)JdbcType.CHAR, (TypeHandler)(new StringTypeHandler()));
        this.register((JdbcType)JdbcType.VARCHAR, (TypeHandler)(new StringTypeHandler()));
        this.register((JdbcType)JdbcType.CLOB, (TypeHandler)(new ClobTypeHandler()));
        this.register((JdbcType)JdbcType.LONGVARCHAR, (TypeHandler)(new ClobTypeHandler()));
        this.register((JdbcType)JdbcType.NVARCHAR, (TypeHandler)(new NStringTypeHandler()));
        this.register((JdbcType)JdbcType.NCHAR, (TypeHandler)(new NStringTypeHandler()));
        this.register((JdbcType)JdbcType.NCLOB, (TypeHandler)(new NClobTypeHandler()));
        this.register((Class)Object.class, JdbcType.ARRAY, (TypeHandler)(new ArrayTypeHandler()));
        this.register((JdbcType)JdbcType.ARRAY, (TypeHandler)(new ArrayTypeHandler()));
        this.register((Class)BigInteger.class, (TypeHandler)(new BigIntegerTypeHandler()));
        this.register((JdbcType)JdbcType.BIGINT, (TypeHandler)(new LongTypeHandler()));
        this.register((Class)BigDecimal.class, (TypeHandler)(new BigDecimalTypeHandler()));
        this.register((JdbcType)JdbcType.REAL, (TypeHandler)(new BigDecimalTypeHandler()));
        this.register((JdbcType)JdbcType.DECIMAL, (TypeHandler)(new BigDecimalTypeHandler()));
        this.register((JdbcType)JdbcType.NUMERIC, (TypeHandler)(new BigDecimalTypeHandler()));
        this.register((Class)InputStream.class, (TypeHandler)(new BlobInputStreamTypeHandler()));
        this.register((Class)Byte[].class, (TypeHandler)(new ByteObjectArrayTypeHandler()));
        this.register((Class)Byte[].class, JdbcType.BLOB, (TypeHandler)(new BlobByteObjectArrayTypeHandler()));
        this.register((Class)Byte[].class, JdbcType.LONGVARBINARY, (TypeHandler)(new BlobByteObjectArrayTypeHandler()));
        this.register((Class)byte[].class, (TypeHandler)(new ByteArrayTypeHandler()));
        this.register((Class)byte[].class, JdbcType.BLOB, (TypeHandler)(new BlobTypeHandler()));
        this.register((Class)byte[].class, JdbcType.LONGVARBINARY, (TypeHandler)(new BlobTypeHandler()));
        this.register((JdbcType)JdbcType.LONGVARBINARY, (TypeHandler)(new BlobTypeHandler()));
        this.register((JdbcType)JdbcType.BLOB, (TypeHandler)(new BlobTypeHandler()));
        this.register(Object.class, this.UNKNOWN_TYPE_HANDLER);
        this.register(Object.class, JdbcType.OTHER, this.UNKNOWN_TYPE_HANDLER);
        this.register(JdbcType.OTHER, this.UNKNOWN_TYPE_HANDLER);
        this.register((Class)Date.class, (TypeHandler)(new DateTypeHandler()));
        this.register((Class)Date.class, JdbcType.DATE, (TypeHandler)(new DateOnlyTypeHandler()));
        this.register((Class)Date.class, JdbcType.TIME, (TypeHandler)(new TimeOnlyTypeHandler()));
        this.register((JdbcType)JdbcType.TIMESTAMP, (TypeHandler)(new DateTypeHandler()));
        this.register((JdbcType)JdbcType.DATE, (TypeHandler)(new DateOnlyTypeHandler()));
        this.register((JdbcType)JdbcType.TIME, (TypeHandler)(new TimeOnlyTypeHandler()));
        this.register((Class)java.sql.Date.class, (TypeHandler)(new SqlDateTypeHandler()));
        this.register((Class)Time.class, (TypeHandler)(new SqlTimeTypeHandler()));
        this.register((Class)Timestamp.class, (TypeHandler)(new SqlTimestampTypeHandler()));
        if (Jdk.dateAndTimeApiExists) {
            Java8TypeHandlersRegistrar.registerDateAndTimeHandlers(this);
        }

        this.register((Class)Character.class, (TypeHandler)(new CharacterTypeHandler()));
        this.register((Class)Character.TYPE, (TypeHandler)(new CharacterTypeHandler()));
    }
	
	// 对于java类型的处理
	public <T> void register(Class<T> javaType, TypeHandler<? extends T> typeHandler) {
        this.register((Type)javaType, (TypeHandler)typeHandler);
    }

	// ---->处理
	private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
			// 首先从typeHandler中获取一个注解,这个注解中包含一个数组,这个数组为jdbcType
	        MappedJdbcTypes mappedJdbcTypes = (MappedJdbcTypes)typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
	        if (mappedJdbcTypes != null) {
	            JdbcType[] var4 = mappedJdbcTypes.value();
	            int var5 = var4.length;
	
	            for(int var6 = 0; var6 < var5; ++var6) {
	                JdbcType handledJdbcType = var4[var6];
	                // 将每一个javaType,jdbcType与handler进行注册
	                this.register(javaType, handledJdbcType, typeHandler);
	            }
				// 如果没有注解,或者注解里没有内容,则以javaType的形式进行注册
	            if (mappedJdbcTypes.includeNullJdbcType()) {
	                this.register((Type)javaType, (JdbcType)null, (TypeHandler)typeHandler);
	            }
	        } else {
	            this.register((Type)javaType, (JdbcType)null, (TypeHandler)typeHandler);
	        }
	
	 }
	
	// 最终处理
	private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
	        if (javaType != null) {
	        	// 先根据jdbcType拿到所有map
	            Map<JdbcType, TypeHandler<?>> map = (Map)this.TYPE_HANDLER_MAP.get(javaType);
	            if (map == null) {
	                map = new HashMap();
	                this.TYPE_HANDLER_MAP.put(javaType, map);
	            }
				// 添加后重新塞入
	            ((Map)map).put(jdbcType, handler);
	        }
			// 最后将javaType和handler放入all中
	        this.ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);
	}
}

自定义实现stringTypeHandler,用于理解 MappedJdbcTypes


// 有多个jdbcType与之对应,但具体的实现(handler)对应一个
@MappedJdbcTypes({JdbcType.CHAR,JdbcType.VARCHAR})
public class StringTypeHandler implements TypeHandler<String> {
    @Override
    public void setParameter(PreparedStatement preparedStatement, int i, String s, JdbcType jdbcType) throws SQLException {

    }
	
	// 例子中只重写了该方法
    @Override
    public String getResult(ResultSet rs, String columnName) throws SQLException {
        return rs.getString(columnName);
    }

    @Override
    public String getResult(ResultSet resultSet, int i) throws SQLException {
        return null;
    }

    @Override
    public String getResult(CallableStatement callableStatement, int i) throws SQLException {
        return null;
    }
}

6、反射工具
mybatis封装后的反射工具可以极其简单实现简单属性以及复杂属性的get和set操作,这在获取参数成员变量,封装返回结果等操作时很有用:

	@Test
    public void testMetaObject(){
        Object user = new User(1, "hhh", 42, new House(120, 1234, 4331));
        
        MetaObject metaObject = MetaObject.forObject(user
                , new DefaultObjectFactory()
                , new DefaultObjectWrapperFactory()
                , new DefaultReflectorFactory());

        // 1、修改user属性
        metaObject.setValue("house.area", 150);
        metaObject.setValue("house.latitude", 9876);
        System.out.println(user);

        // 2、修改当前对象的值
        metaObject.setValue("name", "aaa");
        System.out.println(user);

        // 3、取值 area = #{house.area}
        Object value = metaObject.getValue("house.area");
        System.out.println(value);
        value = metaObject.getValue("house");
        System.out.println(value);
        value = metaObject.getValue("name");
        System.out.println(value);
        
    }

6.1、MetaObject

public class MetaObject {
	// 原始对象
    private final Object originalObject;
    // 包装后的对象
	private final ObjectWrapper objectWrapper;
    // 以下三个工厂我们使用默认就可以了,当然我们可以自由设定
    // 用来实例化对象的工厂
    private final ObjectFactory objectFactory;
    // 用来获取一个包装的对象工厂,给我们扩展使用
    private final ObjectWrapperFactory objectWrapperFactory;
    // 可以为每一个类生成Reflector,他提供了反射的基本能力
    private final ReflectorFactory reflectorFactory;

	private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
        this.originalObject = object;
        this.objectFactory = objectFactory;
        this.objectWrapperFactory = objectWrapperFactory;
        this.reflectorFactory = reflectorFactory;
        // 1、不包装
        if (object instanceof ObjectWrapper) {
            this.objectWrapper = (ObjectWrapper)object;
            // 2、由工厂进行包装
        } else if (objectWrapperFactory.hasWrapperFor(object)) {
            this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
            // 3、框架自行包装
        } else if (object instanceof Map) {
            this.objectWrapper = new MapWrapper(this, (Map)object);
        } else if (object instanceof Collection) {
            this.objectWrapper = new CollectionWrapper(this, (Collection)object);
        } else {
            this.objectWrapper = new BeanWrapper(this, object);
        }
	
	    }
    
	// 对外公开构建方法 
	public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
	        return object == null ? SystemMetaObject.NULL_META_OBJECT : new MetaObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
    }

}

(1)、ObjectFactory
ObjectFactory提供了实例化一个类的能力,可以根据class类型构建对象,默认采用反射的方式:

// DefaultObjectFactory

<T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        try {
            Constructor constructor;
            if (constructorArgTypes != null && constructorArgs != null) {
                constructor = type.getDeclaredConstructor((Class[])constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
                if (!constructor.isAccessible()) {
                    constructor.setAccessible(true);
                }

                return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
            } else {
                constructor = type.getDeclaredConstructor();
                if (!constructor.isAccessible()) {
                    constructor.setAccessible(true);
                }

                return constructor.newInstance();
            }
        } catch (Exception var9) {
            StringBuilder argTypes = new StringBuilder();
            if (constructorArgTypes != null && !constructorArgTypes.isEmpty()) {
                Iterator var6 = constructorArgTypes.iterator();

                while(var6.hasNext()) {
                    Class<?> argType = (Class)var6.next();
                    argTypes.append(argType.getSimpleName());
                    argTypes.append(",");
                }

                argTypes.deleteCharAt(argTypes.length() - 1);
            }

            StringBuilder argValues = new StringBuilder();
            if (constructorArgs != null && !constructorArgs.isEmpty()) {
                Iterator var11 = constructorArgs.iterator();

                while(var11.hasNext()) {
                    Object argValue = var11.next();
                    argValues.append(String.valueOf(argValue));
                    argValues.append(",");
                }

                argValues.deleteCharAt(argValues.length() - 1);
            }

            throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + var9, var9);
        }
    }

(2)、ObjectWrapperFactory
mybatis提供了ObjectWrapperFactory可以对Object的包装过程实现自定义,其默认实现如下,默认实现不会做任何事情,存在的意义是为了扩展,实现自定义

public class DefaultObjectWrapperFactory implements ObjectWrapperFactory {
    public DefaultObjectWrapperFactory() {
    }

    public boolean hasWrapperFor(Object object) {
    	// 默认返回不能包装
        return false;
    }

    public ObjectWrapper getWrapperFor(MetaObject metaObject, Object object) {
    	// 永远不应该调用DefaultObjectWrapperFactory来提供ObjectWrapper
        throw new ReflectionException("The DefaultObjectWrapperFactory should never be called to provide an ObjectWrapper.");
    }
}

(3)、ObjectWrapper
在上面的例子中,操作对象大多是通过ObjectWapper进行的,他的继承结构如下,以BeanWrapper为例,分析下原理
在这里插入图片描述
从源码中得知,BeanWrapper维护了一个object实例本身,一个metaClass(用来进行反射操作)

public class BeanWrapper extends BaseWrapper {
    private final Object object;
    private final MetaClass metaClass;

    public BeanWrapper(MetaObject metaObject, Object object) {
        super(metaObject);
        this.object = object;
        this.metaClass = MetaClass.forClass(object.getClass(), metaObject.getReflectorFactory());
    }
}

MetaClass的构建过程如下,一个metaclass实例维护了一个reflectorFactory用来提供reflector,而reflector是真正的反射工具

public class MetaClass {
    private final ReflectorFactory reflectorFactory;
    private final Reflector reflector;

    private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {
        this.reflectorFactory = reflectorFactory;
        this.reflector = reflectorFactory.findForClass(type);
    }

    public static MetaClass forClass(Class<?> type, ReflectorFactory reflectorFactory) {
        return new MetaClass(type, reflectorFactory);
    }
    
}

(4)、ReflectorFactory
ReflectorFactory维护了我们需要的Reflector,并提供了创建方式,其默认实现是DefaultReflectorFactory,ReflectorFactory也可以进行自定义。

public class DefaultReflectorFactory implements ReflectorFactory {
    private boolean classCacheEnabled = true;
    private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap();

    public DefaultReflectorFactory() {
    }

    public boolean isClassCacheEnabled() {
        return this.classCacheEnabled;
    }

    public void setClassCacheEnabled(boolean classCacheEnabled) {
        this.classCacheEnabled = classCacheEnabled;
    }

    public Reflector findForClass(Class<?> type) {
        if (this.classCacheEnabled) {
            Reflector cached = (Reflector)this.reflectorMap.get(type);
            if (cached == null) {
                cached = new Reflector(type);
                this.reflectorMap.put(type, cached);
            }

            return cached;
        } else {
            return new Reflector(type);
        }
    }
}

(5)、Reflector
Reflector是一个反射工具,里边缓存了一个类的类型,setter方法,getter方法,默认构造器:

public class Reflector {
    private final Class<?> type;
    private final String[] readablePropertyNames;
    private final String[] writeablePropertyNames;
    private final Map<String, Invoker> setMethods = new HashMap();
    private final Map<String, Invoker> getMethods = new HashMap();
    private final Map<String, Class<?>> setTypes = new HashMap();
    private final Map<String, Class<?>> getTypes = new HashMap();
    private Constructor<?> defaultConstructor;
    private Map<String, String> caseInsensitivePropertyMap = new HashMap();
	
	// 构建过程
    public Reflector(Class<?> clazz) {
        this.type = clazz;
        this.addDefaultConstructor(clazz);
        this.addGetMethods(clazz);
        this.addSetMethods(clazz);
        this.addFields(clazz);
        this.readablePropertyNames = (String[])this.getMethods.keySet().toArray(new String[this.getMethods.keySet().size()]);
        this.writeablePropertyNames = (String[])this.setMethods.keySet().toArray(new String[this.setMethods.keySet().size()]);
        String[] var2 = this.readablePropertyNames;
        int var3 = var2.length;

        int var4;
        String propName;
        for(var4 = 0; var4 < var3; ++var4) {
            propName = var2[var4];
            this.caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
        }

        var2 = this.writeablePropertyNames;
        var3 = var2.length;

        for(var4 = 0; var4 < var3; ++var4) {
            propName = var2[var4];
            this.caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
        }

    }
}

6.2、复杂属性的赋值

metaObject.setValue("house.area", 150);

(1)实现过程

// setter,赋值操作
public void setValue(String name, Object value) {
		// 属性解析器,用于解析属性
        PropertyTokenizer prop = new PropertyTokenizer(name);
        // 如果是非简单属性,如:house.area
        if (prop.hasNext()) {
       	    // 如果area没有值,需要先实例化area
            MetaObject metaValue = this.metaObjectForProperty(prop.getIndexedName());
            if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
                if (value == null && prop.getChildren() != null) {
                	// 如果value为null,不要实例化子属性 
                    return;
                }
				// 使用objectFactory构建一个user实例
				// objectFactory的默认实现就是使用反射创建一个实例
                metaValue = this.objectWrapper.instantiatePropertyValue(name, prop, this.objectFactory);
            }
			// metaValue:house,prop.getChildren()--> area,给area赋值
            metaValue.setValue(prop.getChildren(), value);
        } else {
        	// 普通属性的赋值
            this.objectWrapper.set(prop, value);
        }

    }

(2)属性分析器(迭代器设计模式)

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.apache.ibatis.reflection.property;

import java.util.Iterator;

public class PropertyTokenizer implements Iterator<PropertyTokenizer> {
    private String name;
    private final String indexedName;
    private String index;
    private final String children;

    public PropertyTokenizer(String fullname) {
    	// '.'
        int delim = fullname.indexOf(46);
        // 说明这个属性中有,如user.house
        if (delim > -1) {
        	// 截取.之前的内容作为name,此处是house
            this.name = fullname.substring(0, delim);
            // .之后的内容作为children,此处是area
            this.children = fullname.substring(delim + 1);
        } else {
        	// 否则就是fullname
            this.name = fullname;
            this.children = null;
        }

        this.indexedName = this.name;
        // 包含 [ 
        delim = this.name.indexOf(91);
        if (delim > -1) {
            this.index = this.name.substring(delim + 1, this.name.length() - 1);
            this.name = this.name.substring(0, delim);
        }

    }
    
	// 继续分析子属性
    public PropertyTokenizer next() {
        return new PropertyTokenizer(this.children);
    }
}

(3)构建成员变量实例
house如果没有实例化,但我们试图为其赋值,就必须通过反射进行实例化

metaValue = this.objectWrapper.instantiatePropertyValue(name, prop, this.objectFactory);

以BeanWapper的实现为例:

public MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory) {
		// 通过属性的名字获取属性类型,
        Class<?> type = this.getSetterType(prop.getName());

        try {
        	// 根据类型创建一个实例
            Object newObject = objectFactory.create(type);
            // 构建一个metaObject
            MetaObject metaValue = MetaObject.forObject(newObject, this.metaObject.getObjectFactory(), this.metaObject.getObjectWrapperFactory(), this.metaObject.getReflectorFactory());
            // 赋值
            this.set(prop, newObject);
            return metaValue;
        } catch (Exception var7) {
            throw new ReflectionException("Cannot set value of property '" + name + "' because '" + name + "' is null and cannot be instantiated on instance of " + type.getName() + ". Cause:" + var7.toString(), var7);
        }
    }

public Class<?> getSetterType(String name) {
        PropertyTokenizer prop = new PropertyTokenizer(name);
        if (prop.hasNext()) {
            MetaObject metaValue = this.metaObject.metaObjectForProperty(prop.getIndexedName());
            return metaValue == SystemMetaObject.NULL_META_OBJECT ? this.metaClass.getSetterType(name) : metaValue.getSetterType(prop.getChildren());
        } else {
            return this.metaClass.getSetterType(name);
        }
    }

7、ErrorContext
在mybatis中为了更好的定位异常或错误,特封装ErrorContext类,用于描述错误信息,他比传统异常机制要好很多,输出的内容可以自由定制,他使用了ThreadLocal,可以非常好的将问题定位到某个线程,同时保证了线程安全。
源码如下:

public class ErrorContext {
    private static final String LINE_SEPARATOR = System.getProperty("line.separator", "\n");
    private static final ThreadLocal<ErrorContext> LOCAL = new ThreadLocal();
    private ErrorContext stored;
    private String resource;
    private String activity;
    private String object;
    private String message;
    private String sql;
    private Throwable cause;
    
	public static ErrorContext instance() {
        ErrorContext context = (ErrorContext)LOCAL.get();
        if (context == null) {
            context = new ErrorContext();
            LOCAL.set(context);
        }

        return context;
    }
    
 	public ErrorContext store() {
        this.stored = this;
        LOCAL.set(new ErrorContext());
        return (ErrorContext)LOCAL.get();
    }


}

ErrorContext负责封装异常上下文,而ExceptionFactory则负责输出异常上下文:

public class ExceptionFactory {
    private ExceptionFactory() {
    }

    public static RuntimeException wrapException(String message, Exception e) {
        return new PersistenceException(ErrorContext.instance().message(message).cause(e).toString(), e);
    }
}

使用示例如下:

@Test
public void testErrorContext(){
    ExecutorService service = Executors.newFixedThreadPool(10);
    CountDownLatch countDownLatch = new CountDownLatch(10);

    for (int i = 0; i < 10; i++) {
        final int j = i;
        service.execute(()-> {
            try {
                // ErrorContext存储在threadLocal中
                ErrorContext.instance()
                        .activity("在第[" + j + "]流程中。")
                        .object(this.getClass().getName())
                        .sql("select xxx fromfd user")
                        .resource("user.xml");
                if(new Random().nextInt(10) > 6){
                    int m = 1/0;
                }
            }catch (Exception e){
                // 该方法从threadLocal中取出ErrorContext
                throw ExceptionFactory.wrapException("sql has errors", e);
            }
            countDownLatch.countDown();
        });
    }
}

三、sql封装
1、MappedStatement
MappedStatement翻译过来是映射语句,其实就是用来封装sql语句的,每个sql标签都会映射成为一个MappedStatement实例,如下是一个sql标签的xml表现形式

<insert id="insert" parameterType="account" flushCache="" keyColumn=""  parameterMap=""
            useGeneratedKeys="" databaseId="" keyProperty="" lang="" statementType="" timeout="">
        insert into account(username, money) values(#{username},#{money})
</insert>
属性描述
id在命名空间中唯一标识符,可以用来引用这条语句
parameterType将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为MyBatis可以通过类型处理器(TypeHandler推断出具体传入语句的参数,默认值为未设置(unset)
parameterMap用于引用外部 parameterMap 的属性,目前已被废弃。
resultType期望从这条语句返回结果的类全限定名或别名,注意,如果返回的是集合,那应该设置为集合内元素的类型,而不是集合本身。resultType和resultMap之间只能同时使用一个
resultMap对外部resultMap的命名引用
flushCache将其设置为true后,只要语句被调用都会导致本地缓存和二级缓存被清空,默认值:false
useCache将其设置为true后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对select元素为true。需要前提开启二级缓存
timeout这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)
fetchSize这是一个驱动的建议值,尝试让驱动程序每次批量返回的结果行数等于这个设置值。默认值为未设置(unset)(依赖驱动)
statementType可选 STATEMENT,PREPARED,或 CALLABLE。这会让mybatis分别使用Statement,PreparedStatement 或CallableStatement。默认值:PREPARED
resultSetTypeFORWARD_ONLY(结果即指针只能向前),SCROLL_INSENSITIVE(结果集指针可以滚动,可执行上一个、下一个、回到第一个等操作)或 DEFAULT(等价于 unset)中的一个,默认值为 unset(依赖数据库驱动)
databaseId如果配置了数据库厂商标识(databaseIdProvider),MyBatis会加载所有不带databaseId或匹配当前databaseId的语句;如果带和不带的语句都有,则不带的会被忽略
resultOrdered这个设置仅针对嵌套结果 select 语句:如果为true,将会假设包含了嵌套结果集或是分组,当返回一个主结果行时,就不会产生对前面结果集的引用。这就使得在获取嵌套结果集的时候不至于内存不够用。默认值:false
resultSets这个设置仅适用于多结果集的情况。它将列出语句执行后返回的结果集并赋予每个结果集一个名称,多个名称之间以逗号分隔
public final class MappedStatement {
	// 源自于哪个mapper文件
    private String resource;
    // 配置文件
    private Configuration configuration;
    // id
    private String id;
    // 每次从服务器获取的数量
    private Integer fetchSize;
    private Integer timeout;
    //  STATEMENT,PREPARED,或 CALLABLE
    private StatementType statementType;
    private ResultSetType resultSetType;
    // 对sql的包装
    private SqlSource sqlSource;
    // 缓存
    private Cache cache;
    private ParameterMap parameterMap;
    private List<ResultMap> resultMaps;
    // 是否需要刷新缓存
    private boolean flushCacheRequired;
    private boolean useCache;
    private boolean resultOrdered;
    // UNKNOW,INSERT,UPDATE,DELETE,SELECT
    private SqlCommandType sqlCommandType;
    // 主键生成器
    private KeyGenerator keyGenerator;
    private String[] keyProperties;
    private String[] keyColumns;
    // 是否存在嵌套查询的结果集
    private boolean hasNestedResultMaps;
    private String databaseId;
    private Log statementLog;
    // 语言驱动
    private LanguageDriver lang;
    // resultSet
    private String[] resultSets;

}

封装MappedStatement样例

@Test
    public void teseMappedStatement() throws Exception{
        // 构建configuration
        InputStream inputStream = Resources.getResourceAsStream("myBatisConfig.xml");
        XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder(inputStream);
        Configuration configuration = xmlConfigBuilder.parse();

        String sql = "select id,name,age from t_user";
        SqlSource sqlSource = new StaticSqlSource(configuration, sql);
        MappedStatement.Builder builder = new MappedStatement.Builder(configuration, "insert", sqlSource, SqlCommandType.SELECT);

        // 设置结果集
        List<ResultMapping> resultMappingList = new ArrayList<>();
        ResultMapping.Builder idBuilder = new ResultMapping.Builder(
                configuration, "id", "id", new IntegerTypeHandler()
        );
        ResultMapping.Builder nameBuilder = new ResultMapping.Builder(
                configuration, "name", "name",String.class
        );
        ResultMapping.Builder ageBuilder = new ResultMapping.Builder(
                configuration, "id", "age", Integer.class
        );

        resultMappingList.add(idBuilder.build());
        resultMappingList.add(nameBuilder.build());
        resultMappingList.add(ageBuilder.build());

        ResultMap resultMap = new ResultMap.Builder(
                configuration, "myResultMap", User.class, resultMappingList
        ).build();

        builder.resultMaps(Arrays.asList(resultMap));
        builder.fetchSize(300);
        builder.databaseId("mysql");

        MappedStatement mappedStatement = builder.build();
        log.info("mappedStatement = " + mappedStatement);

        // 1、去除标签 <if> <where>
        // 2、替换占位符 #{id} --> select * from user where id = ?
        BoundSql boundSql = mappedStatement.getBoundSql(new User(1, null, 33));
        String sql1 = boundSql.getSql();
        System.out.println(sql1);
    }

StringJoiner

@Test
public void testStringJoiner(){
    StringJoiner stringJoiner = new StringJoiner("-");
    stringJoiner.add("1");
    stringJoiner.add("2");
    stringJoiner.add("3");
    stringJoiner.add("4");
    stringJoiner.add("5");
    System.out.println(stringJoiner);
}

mappedStatement.getBoundSql方法解析

public BoundSql getBoundSql(Object parameterObject) {
	// 有四个实现,常用的是DynamicSqlSource和StaticSqlSource
    BoundSql boundSql = this.sqlSource.getBoundSql(parameterObject);
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings == null || parameterMappings.isEmpty()) {
        boundSql = new BoundSql(this.configuration, boundSql.getSql(), this.parameterMap.getParameterMappings(), parameterObject);
    }

    Iterator var4 = boundSql.getParameterMappings().iterator();

    while(var4.hasNext()) {
        ParameterMapping pm = (ParameterMapping)var4.next();
        String rmId = pm.getResultMapId();
        if (rmId != null) {
            ResultMap rm = this.configuration.getResultMap(rmId);
            if (rm != null) {
                this.hasNestedResultMaps |= rm.hasNestedResultMaps();
            }
        }
    }
    return boundSql;
}

StaticSqlSource.getBoundSql

public BoundSql getBoundSql(Object parameterObject) {
	return new BoundSql(this.configuration, this.sql, this.parameterMappings, parameterObject);
}

DynamicSqlSource

public class DynamicSqlSource implements SqlSource {
    private final Configuration configuration;
    private final SqlNode rootSqlNode;

    public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
        this.configuration = configuration;
        this.rootSqlNode = rootSqlNode;
    }

    public BoundSql getBoundSql(Object parameterObject) {
        DynamicContext context = new DynamicContext(this.configuration, parameterObject);
        this.rootSqlNode.apply(context);
        SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(this.configuration);
        Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
        SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
        BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
        Iterator var7 = context.getBindings().entrySet().iterator();

        while(var7.hasNext()) {
            Map.Entry<String, Object> entry = (Map.Entry)var7.next();
            boundSql.setAdditionalParameter((String)entry.getKey(), entry.getValue());
        }

        return boundSql;
    }
}

DynamicContext

// 编译解析sql语句所需要的上下文
public class DynamicContext {
    public static final String PARAMETER_OBJECT_KEY = "_parameter";
    public static final String DATABASE_ID_KEY = "_databaseId";
    // 当前ognl上下文
    private final ContextMap bindings;
    // 用于append sql
    private final StringBuilder sqlBuilder = new StringBuilder();
    private int uniqueNumber = 0;

	// 初始化ognl
	static {
		OgnlRuntime.setPropertyAccessor(ContextMap.class, new ContextAccessor());
	}
	
	// 构造方法
	public DynamicContext(Configuration configuration, Object parameterObject) {
	    if (parameterObject != null && !(parameterObject instanceof Map)) {
	    	// 初始化metaObject,方便赋值取值等操作
	        MetaObject metaObject = configuration.newMetaObject(parameterObject);
	        this.bindings = new ContextMap(metaObject);
	    } else {
	        this.bindings = new ContextMap((MetaObject)null);
	    }
		// 将原始param对象保存在bindings中,
	    this.bindings.put("_parameter", parameterObject);
	    this.bindings.put("_databaseId", configuration.getDatabaseId());
	}
	
	public String getSql() {
		return this.sqlBuilder.toString().trim();
	}
	
	public void appendSql(String sql) {
	    this.sqlBuilder.append(sql);
	    this.sqlBuilder.append(" ");
	}
	
	// ognl表达式所需要的上下文
	static class ContextMap extends HashMap<String, Object> {
	     private static final long serialVersionUID = 2977601501966151582L;
	     private MetaObject parameterMetaObject;
	
	     public ContextMap(MetaObject parameterMetaObject) {
	         this.parameterMetaObject = parameterMetaObject;
	     }
	
	     public Object get(Object key) {
	         String strKey = (String)key;
	         if (super.containsKey(strKey)) {
	             return super.get(strKey);
	         } else {
	             return this.parameterMetaObject != null ? this.parameterMetaObject.getValue(strKey) : null;
	         }
	     }
	}

}

SqlNode
sqlNode封装了XML中的sql节点,对于多个节点的封装,我们统一使用MixedSqlNode,其继承结构如下:
在这里插入图片描述

public interface SqlNode {
    boolean apply(DynamicContext var1);
}

public class MixedSqlNode implements SqlNode {
	// 每一个标签的集合 
    private final List<SqlNode> contents;

    public MixedSqlNode(List<SqlNode> contents) {
        this.contents = contents;
    }

    public boolean apply(DynamicContext context) {
        Iterator var2 = this.contents.iterator();
		// 遍历每一个标签,并传递context进行拼接
        while(var2.hasNext()) {
            SqlNode sqlNode = (SqlNode)var2.next();
            sqlNode.apply(context);
        }

        return true;
    }
}

每一个sqlNode都有不同的职责,其中IfSqlNode使用ognl表达式解析test结果,如果为true,会继续解析if标签中的其他sqlNode,如果为false,直接丢弃。where和set标签主要用于处理前后缀,共同继承自trimSqlNode,trimSqlNode用于除去前后缀,where和set用于定义前后缀的内容。

获取sqlSource(将XNode解析为sqlSource)

<select>
    select * from t_user
    <where>
        <if test="id != null">
            and id = #{id}
        </if>
    </where>
</select>
@Test
public void testXMLLanguageDriver() throws Exception{
    // 构建configuration
    InputStream inputStream = Resources.getResourceAsStream("myBatisConfig.xml");
    XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder(inputStream);
    Configuration configuration = xmlConfigBuilder.parse();

    inputStream = Resources.getResourceAsStream("cn/j3code/studyspring/mybatis/helloworld/mapper/sql.xml");
    XPathParser xPathParser = new XPathParser(inputStream);
    XNode xNode = xPathParser.evalNode("/select");

    XMLLanguageDriver xmlLanguageDriver = new XMLLanguageDriver();
    SqlSource sqlSource = xmlLanguageDriver.createSqlSource(configuration, xNode, User.class);
    System.out.println(sqlSource);
    
}

最终构造的sqlSource结构
在这里插入图片描述
四、配置类的读取流程

@BeforeEach
public void startUp() throws IOException{
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    ClassPathResource resource = new ClassPathResource("myBatisConfig.xml");
    // 1、入口
    SqlSessionFactory sqlSessionFactory = builder.build(resource.getInputStream());
    sqlSession = sqlSessionFactory.openSession();
}

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
       SqlSessionFactory var5;
       try {
           XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
           // 2、parser.parse()最终返回Configuration
           var5 = this.build(parser.parse());
       } catch (Exception var14) {
           throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
       } finally {
           ErrorContext.instance().reset();

           try {
               inputStream.close();
           } catch (IOException var13) {
           }

       }

       return var5;
   }

public Configuration parse() {
     if (this.parsed) {
         throw new BuilderException("Each XMLConfigBuilder can only be used once.");
     } else {
     	 // 3、
     	 // 将解析标志设为true
         this.parsed = true;
         // 获取配置xml中configuration节点
         this.parseConfiguration(this.parser.evalNode("/configuration"));
         return this.configuration;
     }
}

private void parseConfiguration(XNode root) {
	// 4、逐个解析每一个标签并赋值
    try {
        this.propertiesElement(root.evalNode("properties"));
        Properties settings = this.settingsAsProperties(root.evalNode("settings"));
        this.loadCustomVfs(settings);
        this.typeAliasesElement(root.evalNode("typeAliases"));
        this.pluginElement(root.evalNode("plugins"));
        this.objectFactoryElement(root.evalNode("objectFactory"));
        this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
        this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
        this.settingsElement(settings);
        this.environmentsElement(root.evalNode("environments"));
        this.databaseIdProviderElement(root.eva lNode("databaseIdProvider"));
        this.typeHandlerElement(root.evalNode("typeHandlers"));
        this.mapperElement(root.evalNode("mappers"));
    } catch (Exception var3) {
        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
    }
    }


PooledDataSourceFactory

public class PooledDataSourceFactory extends UnpooledDataSourceFactory {
    public PooledDataSourceFactory() {
        this.dataSource = new PooledDataSource();
    }
}

PooledDataSource

public class PooledDataSource implements DataSource {
    private static final Log log = LogFactory.getLog(PooledDataSource.class);
    private final PoolState state = new PoolState(this);
    private final UnpooledDataSource dataSource;
    // 最大连接活跃数
    protected int poolMaximumActiveConnections = 10;
    // 最大空闲连接数
    protected int poolMaximumIdleConnections = 5;
    // 连接最大使用时间
    protected int poolMaximumCheckoutTime = 20000;
    // 最大等待时间
    protected int poolTimeToWait = 20000;
    // 可容忍的最大坏连接数
    protected int poolMaximumLocalBadConnectionTolerance = 3;
    // 探活
    protected String poolPingQuery = "NO PING QUERY SET";
    protected boolean poolPingEnabled;
    protected int poolPingConnectionsNotUsedFor;
    private int expectedConnectionTypeCode;

    public PooledDataSource() {
        this.dataSource = new UnpooledDataSource();
    }

    public PooledDataSource(UnpooledDataSource dataSource) {
        this.dataSource = dataSource;
    }
	
	// 获取连接
	public Connection getConnection() throws SQLException {
	        return this.popConnection(this.dataSource.getUsername(), this.dataSource.getPassword()).getProxyConnection();
	}
	
	 private PooledConnection popConnection(String username, String password) throws SQLException {
        boolean countedWait = false;
        PooledConnection conn = null;
        long t = System.currentTimeMillis();
        int localBadConnectionCount = 0;

        while(conn == null) {
            synchronized(this.state) {
                PoolState var10000;
                // 空闲的连接不为空,直接从空闲连接中拿一个返回
                if (!this.state.idleConnections.isEmpty()) {
                    conn = (PooledConnection)this.state.idleConnections.remove(0);
                    if (log.isDebugEnabled()) {
                        log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
                    }
                // 当前活跃数<池子允许的最大活跃数 
                } else if (this.state.activeConnections.size() < this.poolMaximumActiveConnections) {
                    conn = new PooledConnection(this.dataSource.getConnection(), this);
                    if (log.isDebugEnabled()) {
                        log.debug("Created connection " + conn.getRealHashCode() + ".");
                    }
                // 当前活跃数=池子允许的最大活跃数 
                } else {
                	// 拿到最开始的连接(最先创建的连接) 
                    PooledConnection oldestActiveConnection = (PooledConnection)this.state.activeConnections.get(0);
                    // 判断最老的连接已经使用的时间
                    long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
                    // 若这个时间>允许使用的最大时间
                    if (longestCheckoutTime > (long)this.poolMaximumCheckoutTime) {
                    	// state中相应状态连接数+1
                        ++this.state.claimedOverdueConnectionCount;
                        var10000 = this.state;
                        var10000.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
                        var10000 = this.state;
                        var10000.accumulatedCheckoutTime += longestCheckoutTime;
                        // 从activeConnections中移除该连接
                        this.state.activeConnections.remove(oldestActiveConnection);
                        // 若该连接不是自动提交的
                        if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
                            try {
                            	// 将操作进行回滚 
                                oldestActiveConnection.getRealConnection().rollback();
                            } catch (SQLException var16) {
                                log.debug("Bad connection. Could not roll back");
                            }
                        }
						// 重新new一个新的连接
                        conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
                        conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp());
                        conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp());
                        // 将旧的连接置为不可用
                        oldestActiveConnection.invalidate();
                        if (log.isDebugEnabled()) {
                            log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
                        }
                    } else {
                    	// 第一个连接也没超时,等待
                        try {
                            if (!countedWait) {
                            	// 必须去等待数量+1 
                                ++this.state.hadToWaitCount;
                                // 等待标志置为true
                                countedWait = true;
                            }

                            if (log.isDebugEnabled()) {
                                log.debug("Waiting as long as " + this.poolTimeToWait + " milliseconds for connection.");
                            }

                            long wt = System.currentTimeMillis();
                            // 开始等待
                            this.state.wait((long)this.poolTimeToWait);
                            var10000 = this.state;
                            var10000.accumulatedWaitTime += System.currentTimeMillis() - wt;
                        } catch (InterruptedException var17) {
                            break;
                        }
                    }
                }
				
				// 已经获得连接
                if (conn != null) {
                	// 测试连接是否可用(ping)
                    if (conn.isValid()) {
                    	// 连接是不是自动提交
                        if (!conn.getRealConnection().getAutoCommit()) {
                            // 先回滚
                            conn.getRealConnection().rollback();
                        }


						// 设置连接类型                        
						conn.setConnectionTypeCode(this.assembleConnectionTypeCode(this.dataSource.getUrl(), username, password));
                        // 设置连接使用时间
                        conn.setCheckoutTimestamp(System.currentTimeMillis());
                        conn.setLastUsedTimestamp(System.currentTimeMillis());
                        // 修改池状态
                        this.state.activeConnections.add(conn);
                        ++this.state.requestCount;
                        var10000 = this.state;
                        var10000.accumulatedRequestTime += System.currentTimeMillis() - t;
                    } else {
                    	// 如果连接不可用
                        if (log.isDebugEnabled()) {
                            log.debug("A bad connection (" + conn.getRealHashCode() + ") was returned from the pool, getting another connection.");
                        }
						// 对坏连接进行统计
                        ++this.state.badConnectionCount;
                        ++localBadConnectionCount;
                        conn = null;
                        // 如果本次最大坏的连接数>最大的空闲连接数+最大可容忍的坏的连接数
                        if (localBadConnectionCount > this.poolMaximumIdleConnections + this.poolMaximumLocalBadConnectionTolerance) {
                            if (log.isDebugEnabled()) {
                                log.debug("PooledDataSource: Could not get a good connection to the database.");
                            }
							// 抛异常
                            throw new SQLException("PooledDataSource: Could not get a good connection to the database.");
                        }
                    }
                }
            }
        }

        if (conn == null) {
            if (log.isDebugEnabled()) {
                log.debug("PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");
            }

            throw new SQLException("PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");
        } else {
            return conn;
        }
    }
	

}

PoolState

// 保存了当前pool的一些状态
public class PoolState {
    protected PooledDataSource dataSource;
    // 空闲的连接
    protected final List<PooledConnection> idleConnections = new ArrayList();
    // 活跃的连接
    protected final List<PooledConnection> activeConnections = new ArrayList();
    // 当前池子被请求的次数
    protected long requestCount = 0L;
    // 累计的请求时间
    protected long accumulatedRequestTime = 0L;
    // 累计的使用时间(连接在外部被使用的时间)
    protected long accumulatedCheckoutTime = 0L;
    // 过期未还的连接数量
    protected long claimedOverdueConnectionCount = 0L;
    // 累计的未还的连接的使用时间
    protected long accumulatedCheckoutTimeOfOverdueConnections = 0L;
    // 累计的等待时长
    protected long accumulatedWaitTime = 0L;
    // 必须去等待的数量
    protected long hadToWaitCount = 0L;
    // 坏的连接数量
    protected long badConnectionCount = 0L;

    public PoolState(PooledDataSource dataSource) {
        this.dataSource = dataSource;
    }
}

归还连接


// org.apache.ibatis.datasource.pooled.PooledConnection#invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        // 若调用的方法为close
        if ("close".hashCode() == methodName.hashCode() && "close".equals(methodName)) {
            this.dataSource.pushConnection(this);
            return null;
        } else {
            try {
                if (!Object.class.equals(method.getDeclaringClass())) {
                    this.checkConnection();
                }

                return method.invoke(this.realConnection, args);
            } catch (Throwable var6) {
                throw ExceptionUtil.unwrapThrowable(var6);
            }
        }
    }

// 归还连接
protected void pushConnection(PooledConnection conn) throws SQLException {
        synchronized(this.state) {
        	// 从活跃连接中删除该连接
            this.state.activeConnections.remove(conn);
            // 当前连接可用
            if (conn.isValid()) {
                PoolState var10000;
                // 若空闲连接的数量<容器允许的最大的空闲连接数 && 
                if (this.state.idleConnections.size() < this.poolMaximumIdleConnections && conn.getConnectionTypeCode() == this.expectedConnectionTypeCode) {
                	// 做统计
                    var10000 = this.state;
                    var10000.accumulatedCheckoutTime += conn.getCheckoutTime();
                    // 回滚连接
                    if (!conn.getRealConnection().getAutoCommit()) {
                        conn.getRealConnection().rollback();
                    }
					// 重新包装
                    PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);				
                    // 将连接放到空闲连接
                    this.state.idleConnections.add(newConn);
                    newConn.setCreatedTimestamp(conn.getCreatedTimestamp());
                    newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());
                    // 将原来连接置为不可用
                    conn.invalidate();
                    if (log.isDebugEnabled()) {
                        log.debug("Returned connection " + newConn.getRealHashCode() + " to pool.");
                    }
					// 唤醒state上等待的线程
                    this.state.notifyAll();
                } else {
                	// 空闲的连接已经满了
                	// 统计
                    var10000 = this.state;
                    var10000.accumulatedCheckoutTime += conn.getCheckoutTime();
                    if (!conn.getRealConnection().getAutoCommit()) {
                        conn.getRealConnection().rollback();
                    }
					// 将连接断开
                    conn.getRealConnection().close();
                    if (log.isDebugEnabled()) {
                        log.debug("Closed connection " + conn.getRealHashCode() + ".");
                    }
					// 该连接置为不可用
                    conn.invalidate();
                }
            } else {
                if (log.isDebugEnabled()) {
                    log.debug("A bad connection (" + conn.getRealHashCode() + ") attempted to return to the pool, discarding connection.");
                }

                ++this.state.badConnectionCount;
            }

        }
    }

解析Mappers标签

 // 解析mapper入口 
 this.mapperElement(root.evalNode("mappers"));
 // 可配置方式1
 <mapper resource="cn/mybatis/helloworld/mapper/UserMapper.xml"/>
 // 可配置方式2
 <package name="文件所在包路径"></package>
  
private void mapperElement(XNode parent) throws Exception {
        if (parent != null) {
            Iterator var2 = parent.getChildren().iterator();

            while(true) {
                while(var2.hasNext()) {
                	// 拿到子元素 
                    XNode child = (XNode)var2.next();
                    String resource;
                    // 判断子元素是是否是package
                    if ("package".equals(child.getName())) {
                        resource = child.getStringAttribute("name");
                        this.configuration.addMappers(resource);
                    } else {
                        resource = child.getStringAttribute("resource");
                        String url = child.getStringAttribute("url");
                        String mapperClass = child.getStringAttribute("class");
                        XMLMapperBuilder mapperParser;
                        InputStream inputStream;
                        // 配的resource
                        if (resource != null && url == null && mapperClass == null) {
                        	// 解析资源时,在当前线程下封装ErrorContext,若出问题,则告知可能该resource有问题
                            ErrorContext.instance().resource(resource);
                            inputStream = Resources.getResourceAsStream(resource);
                            mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
                            mapperParser.parse();
                        // 配的url 
                        } else if (resource == null && url != null && mapperClass == null) {
                            ErrorContext.instance().resource(url);
                            inputStream = Resources.getUrlAsStream(url);
                            mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());
                            mapperParser.parse();
                        } else {
                        	// 配了多个/没配
                            if (resource != null || url != null || mapperClass == null) {
                                throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                            }
							// 配了class
                            Class<?> mapperInterface = Resources.classForName(mapperClass);
                            this.configuration.addMapper(mapperInterface);
                        }
                    }
                }

                return;
            }
        }
    }

mapperParser.parse();

public void parse() {
	// 若该mapper未被解析,则进行解析
    if (!this.configuration.isResourceLoaded(this.resource)) {
        this.configurationElement(this.parser.evalNode("/mapper"));
        // 将资源即mapper.xml放入configuration(下次需要加载时直接拿)
        this.configuration.addLoadedResource(this.resource);
        // 将mapper和命名空间绑定
        this.bindMapperForNamespace();
    }

    this.parsePendingResultMaps();
    this.parsePendingCacheRefs();
    this.parsePendingStatements();
}

private void configurationElement(XNode context) {
	// 逐个获取标签进行解析
    try {
        String namespace = context.getStringAttribute("namespace");
        if (namespace != null && !namespace.equals("")) {
            this.builderAssistant.setCurrentNamespace(namespace);
            this.cacheRefElement(context.evalNode("cache-ref"));
            this.cacheElement(context.evalNode("cache"));
            this.parameterMapElement(context.evalNodes("/mapper/parameterMap"));
            this.resultMapElements(context.evalNodes("/mapper/resultMap"));
            this.sqlElement(context.evalNodes("/mapper/sql"));
            this.buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
        } else {
            throw new BuilderException("Mapper's namespace cannot be empty");
        }
    } catch (Exception var3) {
        throw new BuilderException("Error parsing Mapper XML. Cause: " + var3, var3);
    }
}

阻塞式缓存
特点:调用get方法获取同一个key时,需要进行阻塞等待

public class BlockingCache implements Cache {
	// 超时时间
    private long timeout;
    // 被装饰者
    private final Cache delegate;
    // 针对每一个key建立一个ReentrantLock
    private final ConcurrentHashMap<Object, ReentrantLock> locks;

    public BlockingCache(Cache delegate) {
        this.delegate = delegate;
        this.locks = new ConcurrentHashMap();
    }
    
public Object getObject(Object key) {
	// 获取锁
    this.acquireLock(key);
    // 执行数据库操作
    Object value = this.delegate.getObject(key);
    if (value != null) {
        this.releaseLock(key);
    }

    return value;
}

private ReentrantLock getLockForKey(Object key) {
        ReentrantLock lock = new ReentrantLock();
        // 如果指定的key已存在,则不会put,返回这个key对应的value,
        // 如果指定的key不存在,则返回null
        ReentrantLock previous = (ReentrantLock)this.locks.putIfAbsent(key, lock);
        // 如果previous == null,说明是第一次访问
        return previous == null ? lock : previous;
}

private void acquireLock(Object key) {
		// 获取绑定在该key上的ReentrantLock 
        Lock lock = this.getLockForKey(key);
        if (this.timeout > 0L) {
            try {
                boolean acquired = lock.tryLock(this.timeout, TimeUnit.MILLISECONDS);
                if (!acquired) {
                    throw new CacheException("Couldn't get a lock in " + this.timeout + " for the key " + key + " at the cache " + this.delegate.getId());
                }
            } catch (InterruptedException var4) {
                throw new CacheException("Got interrupted while trying to acquire lock for key " + key, var4);
            }
        } else {
            lock.lock();
        }

    }

}

private void releaseLock(Object key) {
        ReentrantLock lock = (ReentrantLock)this.locks.get(key);
        if (lock.isHeldByCurrentThread()) {
            lock.unlock();
        }

}

public void putObject(Object key, Object value) {
        try {
            this.delegate.putObject(key, value);
        } finally {
            this.releaseLock(key);
        }

    }

五、理解sqlSession
1、sqlSession = sqlSessionFactory.openSession();
DefaultSqlSessionFactory

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        Transaction tx = null;

        DefaultSqlSession var8;
        try {
            Environment environment = this.configuration.getEnvironment();
            // 拿到事务工厂
            TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
            tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
            // 执行器
            Executor executor = this.configuration.newExecutor(tx, execType);
            var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
        } catch (Exception var12) {
            this.closeTransaction(tx);
            throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);
        } finally {
            ErrorContext.instance().reset();
        }

        return var8;
    }

TransactionFactory的实现
在这里插入图片描述
ManagedTransactionFactory:空壳事务管理器,用于继承后自行扩展;
SpringManagedTransactionFactory:spring实现的事务工厂;
JdbcTransactionFactory:mybatis实现的简单事务工厂;

Executor


// 里面包含一些具体的sql操作
public interface Executor {
    ResultHandler NO_RESULT_HANDLER = null;

    int update(MappedStatement var1, Object var2) throws SQLException;

    <E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4, CacheKey var5, BoundSql var6) throws SQLException;

    <E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4) throws SQLException;

    <E> Cursor<E> queryCursor(MappedStatement var1, Object var2, RowBounds var3) throws SQLException;

    List<BatchResult> flushStatements() throws SQLException;

    void commit(boolean var1) throws SQLException;

    void rollback(boolean var1) throws SQLException;

    CacheKey createCacheKey(MappedStatement var1, Object var2, RowBounds var3, BoundSql var4);

    boolean isCached(MappedStatement var1, CacheKey var2);

    void clearLocalCache();

    void deferLoad(MappedStatement var1, MetaObject var2, String var3, CacheKey var4, Class<?> var5);

    Transaction getTransaction();

    void close(boolean var1);

    boolean isClosed();

    void setExecutorWrapper(Executor var1);
}

在这里插入图片描述
Mybatis提供了三种sql执行器,分别是SIMPLE(默认)、REUSE、BATCH。CachingExecutor只负责管理缓存

  • SIMPLE(SimpleExecutor),相当于JDBC的stmt.execute(sql) 执行完毕即关闭
  • REUSE(ReuseExecutor),相当于JDBC的stmt.execute(sql) 执行完不关闭,而是将stmt存入 Map<String,Statement> 中缓存,其中key为执行的sql模板;
  • BATCH (BatchExecutor),相当于JDBC语句的 stmt.addBatch(sql),即仅将执行SQL加入到批量计划但不真正执行,所以此时不会执行返回受影响行数,而只有执行stmt.executeBatch()后才会真正执行sql。

Executor executor = this.configuration.newExecutor(tx, execType);

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
        executorType = executorType == null ? this.defaultExecutorType : executorType;
        executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
        Object executor;
        if (ExecutorType.BATCH == executorType) {
            executor = new BatchExecutor(this, transaction);
        } else if (ExecutorType.REUSE == executorType) {
            executor = new ReuseExecutor(this, transaction);
        } else {
            executor = new SimpleExecutor(this, transaction);
        }

        if (this.cacheEnabled) {
            executor = new CachingExecutor((Executor)executor);
        }

        Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
        return executor;
    }

Executor执行流程
执行器在执行query方法时,可以分为三个步骤:

  • 解析获取sql
  • 执行sql
  • 处理结果集

1、CachingExecutor.query

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
		// 拿到绑定sql
        BoundSql boundSql = ms.getBoundSql(parameterObject);
        // 根据ms,参数,分页信息,boundSql生成一个缓存的key
        CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql);
        return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    }

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
		// 从ms中拿到cache(mepper.xml中配置的cache)
        Cache cache = ms.getCache();
        if (cache != null) {
            this.flushCacheIfRequired(ms);
            if (ms.isUseCache() && resultHandler == null) {
                this.ensureNoOutParams(ms, parameterObject, boundSql);
                List<E> list = (List)this.tcm.getObject(cache, key);
                if (list == null) {
                    list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                    this.tcm.putObject(cache, key, list);
                }

                return list;
            }
        }
		// 未配置二级缓存则不生效
        return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    }

// BaseExecutor.query
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
        if (this.closed) {
            throw new ExecutorException("Executor was closed.");
        } else {
        	// isFlushCacheRequired:是否sql执行完成刷新缓存(sql上的配置)
        	// this.queryStack == 0:避免在嵌套查询的过程中把有必要存在的缓存刷新掉
            if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
                this.clearLocalCache();
            }

            List list;
            try {
                ++this.queryStack;
                // 去一级缓存中拿
                list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
                if (list != null) {
                	// 将输出参数进行保存
                    this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
                } else {
                	// 到数据库中查
                    list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
                }
            } finally {
                --this.queryStack;
            }

            if (this.queryStack == 0) {
                Iterator var8 = this.deferredLoads.iterator();

                while(var8.hasNext()) {
                    DeferredLoad deferredLoad = (DeferredLoad)var8.next();
                    deferredLoad.load();
                }

                this.deferredLoads.clear();
                // 如果当前缓存配置为STATEMENT,清除缓存   
                if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
                    this.clearLocalCache();
                }
            }

            return list;
        }
    }

// 从数据库里查询数据
 private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        // 先用占位符占位置,存在嵌套查询时,有些其他语句需要当前数据,拿到占位符,放到deferLoad,最终查询完毕,统一从一级缓存拿到
        this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);

        List list;
        try {
            list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
        } finally {
            this.localCache.removeObject(key);
        }

        this.localCache.putObject(key, list);
        if (ms.getStatementType() == StatementType.CALLABLE) {
            this.localOutputParameterCache.putObject(key, parameter);
        }

        return list;
    }
    
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Statement stmt = null;

        List var9;
        try {
            Configuration configuration = ms.getConfiguration();
            StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
            stmt = this.prepareStatement(handler, ms.getStatementLog());
            var9 = handler.query(stmt, resultHandler);
        } finally {
            this.closeStatement(stmt);
        }

        return var9;
    }

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        String sql = this.boundSql.getSql();
        statement.execute(sql);
        return this.resultSetHandler.handleResultSets(statement);
    }

public List<Object> handleResultSets(Statement stmt) throws SQLException {
        ErrorContext.instance().activity("handling results").object(this.mappedStatement.getId());
        List<Object> multipleResults = new ArrayList();
        int resultSetCount = 0;
        ResultSetWrapper rsw = this.getFirstResultSet(stmt);
        List<ResultMap> resultMaps = this.mappedStatement.getResultMaps();
        int resultMapCount = resultMaps.size();
        this.validateResultMapsCount(rsw, resultMapCount);

        while(rsw != null && resultMapCount > resultSetCount) {
            ResultMap resultMap = (ResultMap)resultMaps.get(resultSetCount);
            this.handleResultSet(rsw, resultMap, multipleResults, (ResultMapping)null);
            rsw = this.getNextResultSet(stmt);
            this.cleanUpAfterHandlingResultSet();
            ++resultSetCount;
        }

        String[] resultSets = this.mappedStatement.getResultSets();
        if (resultSets != null) {
            while(rsw != null && resultSetCount < resultSets.length) {
                ResultMapping parentMapping = (ResultMapping)this.nextResultMaps.get(resultSets[resultSetCount]);
                if (parentMapping != null) {
                    String nestedResultMapId = parentMapping.getNestedResultMapId();
                    ResultMap resultMap = this.configuration.getResultMap(nestedResultMapId);
                    this.handleResultSet(rsw, resultMap, (List)null, parentMapping);
                }

                rsw = this.getNextResultSet(stmt);
                this.cleanUpAfterHandlingResultSet();
                ++resultSetCount;
            }
        }

        return this.collapseSingleResultList(multipleResults);
    }

private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
        try {
            if (parentMapping != null) {
                this.handleRowValues(rsw, resultMap, (ResultHandler)null, RowBounds.DEFAULT, parentMapping);
            } else if (this.resultHandler == null) {
                DefaultResultHandler defaultResultHandler = new DefaultResultHandler(this.objectFactory);
                this.handleRowValues(rsw, resultMap, defaultResultHandler, this.rowBounds, (ResultMapping)null);
                multipleResults.add(defaultResultHandler.getResultList());
            } else {
                this.handleRowValues(rsw, resultMap, this.resultHandler, this.rowBounds, (ResultMapping)null);
            }
        } finally {
            this.closeResultSet(rsw.getResultSet());
        }

    }

public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
        if (resultMap.hasNestedResultMaps()) {
            this.ensureNoRowBounds();
            this.checkResultHandler();
            this.handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
        } else {
            this.handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
        }

    }
    
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
        DefaultResultContext<Object> resultContext = new DefaultResultContext();
        this.skipRows(rsw.getResultSet(), rowBounds);

        while(this.shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
            ResultMap discriminatedResultMap = this.resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, (String)null);
            Object rowValue = this.getRowValue(rsw, discriminatedResultMap);
            this.storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
        }

    }

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
        ResultLoaderMap lazyLoader = new ResultLoaderMap();
        Object rowValue = this.createResultObject(rsw, resultMap, lazyLoader, (String)null);
        if (rowValue != null && !this.hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
            MetaObject metaObject = this.configuration.newMetaObject(rowValue);
            boolean foundValues = this.useConstructorMappings;
            if (this.shouldApplyAutomaticMappings(resultMap, false)) {
                foundValues = this.applyAutomaticMappings(rsw, resultMap, metaObject, (String)null) || foundValues;
            }

            foundValues = this.applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, (String)null) || foundValues;
            foundValues = lazyLoader.size() > 0 || foundValues;
            rowValue = !foundValues && !this.configuration.isReturnInstanceForEmptyRow() ? null : rowValue;
        }

        return rowValue;
    }

query执行逻辑总结

  • query方法会包装成一个CachingExecutor(需要配置中开启二级缓存才生效)
  • 根据ms,查询参数,分页信息,boundsql构造一个用于缓存的key(二级缓存)
  • 当二级缓存未配置开启或未拿到缓存数据时,走BaseExecutor.query
  • baseExecutor中提供了查询模板(针对一级缓存:deferredLoad和嵌套查询:queryStack)。先在以及缓存中拿,未拿到走查询

六、懒加载
在mybatis中增加如下配置可实现懒加载

		<setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="true"/>
        <setting name="lazyLoadTriggerMethods" value=""/>
  • lazyLoadingEnabled:懒加载的全局开关。如果设置为false,则相关的sql会立即执行,默认为false
  • aggressiveLazyLoading:急切的执行懒加载,设置为true时,只要触发了懒加载就会将该层及其级联的查询全部执行,默认为true。设置为false时,每个属性都按需加载,这个配置适用于多层级联的懒加载
  • lazyLoadTriggerMethods:触发一次性加载的方法
  // DefaultResultSetHandler.getNestedQueryMappingValue
  ResultLoader resultLoader = new ResultLoader(this.configuration, this.executor, nestedQuery,     nestedQueryParameterObject, targetType, key, nestedBoundSql);
  if (propertyMapping.isLazy()) {
  	// 开启懒加载时,会将resultLoader放入loaderMap中,需要时再调用resultLoader.loadResult进行加载
      lazyLoader.addLoader(property, metaResultObject, resultLoader);
      value = DEFERED;
  } else {
  	  // 否则直接进行加载 
      value = resultLoader.loadResult();
  }

public void addLoader(String property, MetaObject metaResultObject, ResultLoader resultLoader) {
        String upperFirst = getUppercaseFirstProperty(property);
        if (!upperFirst.equalsIgnoreCase(property) && this.loaderMap.containsKey(upperFirst)) {
            throw new ExecutorException("Nested lazy loaded result property '" + property + "' for query id '" + resultLoader.mappedStatement.getId() + " already exists in the result map. The leftmost property of all lazy loaded properties must be unique within a result map.");
        } else {
            this.loaderMap.put(upperFirst, new LoadPair(property, metaResultObject, resultLoader));
        }
    }

加载时机:在使用返回值或使用返回值中和级联字段相关的get,set方法时进行加载。因此,具体获取结果的对象一定为代理对象,详见代码如下:

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
        ResultLoaderMap lazyLoader = new ResultLoaderMap();
        // 此时获取的rowValue为代理对象
        Object rowValue = this.createResultObject(rsw, resultMap, lazyLoader, (String)null);
        if (rowValue != null && !this.hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
            MetaObject metaObject = this.configuration.newMetaObject(rowValue);
            boolean foundValues = this.useConstructorMappings;
            if (this.shouldApplyAutomaticMappings(resultMap, false)) {
                foundValues = this.applyAutomaticMappings(rsw, resultMap, metaObject, (String)null) || foundValues;
            }

            foundValues = this.applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, (String)null) || foundValues;
            foundValues = lazyLoader.size() > 0 || foundValues;
            rowValue = !foundValues && !this.configuration.isReturnInstanceForEmptyRow() ? null : rowValue;
        }

        return rowValue;
    }

 private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
        this.useConstructorMappings = false;
        List<Class<?>> constructorArgTypes = new ArrayList();
        List<Object> constructorArgs = new ArrayList();
        // 创建结果普通实例
        Object resultObject = this.createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
        if (resultObject != null && !this.hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
            List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
            Iterator var9 = propertyMappings.iterator();

            while(var9.hasNext()) {
                ResultMapping propertyMapping = (ResultMapping)var9.next();
                if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
                	// 创建代理结果
                    resultObject = this.configuration.getProxyFactory().createProxy(resultObject, lazyLoader, this.configuration, this.objectFactory, constructorArgTypes, constructorArgs);
                    break;
                }
            }
        }

        this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty();
        return resultObject;
    }

public Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        return JavassistProxyFactory.EnhancedResultObjectProxyImpl.createProxy(target, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
    }

// EnhancedResultObjectProxyImpl
public Object invoke(Object enhanced, Method method, Method methodProxy, Object[] args) throws Throwable {
            String methodName = method.getName();

            try {
                synchronized(this.lazyLoader) {
                    if ("writeReplace".equals(methodName)) {
                        Object original;
                        if (this.constructorArgTypes.isEmpty()) {
                            original = this.objectFactory.create(this.type);
                        } else {
                            original = this.objectFactory.create(this.type, this.constructorArgTypes, this.constructorArgs);
                        }

                        PropertyCopier.copyBeanProperties(this.type, enhanced, original);
                        if (this.lazyLoader.size() > 0) {
                            return new JavassistSerialStateHolder(original, this.lazyLoader.getProperties(), this.objectFactory, this.constructorArgTypes, this.constructorArgs);
                        }

                        return original;
                    }

                    if (this.lazyLoader.size() > 0 && !"finalize".equals(methodName)) {
                        if (!this.aggressive && !this.lazyLoadTriggerMethods.contains(methodName)) {
                            String property;
                            // 调用set方法,懒加载会被取消
                            if (PropertyNamer.isSetter(methodName)) {
                                property = PropertyNamer.methodToProperty(methodName);
                                this.lazyLoader.remove(property);
                            } else if (PropertyNamer.isGetter(methodName)) {
                            	// 如果为get方法,拿到Property
                                property = PropertyNamer.methodToProperty(methodName);
                                // 判断lazyLoader中是否有该property,有则加载
                                if (this.lazyLoader.hasLoader(property)) {
                                    this.lazyLoader.load(property);
                                }
                            }
                        } else {
                        	// 触发一次性加载 
                            this.lazyLoader.loadAll();
                        }
                    }
                }

                return methodProxy.invoke(enhanced, args);
            } catch (Throwable var10) {
                throw ExceptionUtil.unwrapThrowable(var10);
            }
        }

七、动态代理
mybatis提供了非常强大的动态代理能力,我们可以使用getMapper方法获取一个代理对象,更加方便我们执行增删改查操作

 public void testProcy() throws IOException{
        try {
        	// mapper即为代理对象
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            int rows = mapper.insert(new User(null, "JERRY", 19));
            sqlSession.commit();
            log.info("rows -> {}", rows);
        }catch (Exception e){
            e.printStackTrace();
            sqlSession.rollback();
        }finally {
            sqlSession.close();
        }
    }

public <T> T getMapper(Class<T> type) {
	return this.configuration.getMapper(type, this);
}

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
	return this.mapperRegistry.getMapper(type, sqlSession);
}

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
    if (mapperProxyFactory == null) {
        throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    } else {
        try {
            return mapperProxyFactory.newInstance(sqlSession);
        } catch (Exception var5) {
            throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
        }
    }
}

public T newInstance(SqlSession sqlSession) {
    MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
    return this.newInstance(mapperProxy);
}

protected T newInstance(MapperProxy<T> mapperProxy) {
	return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}

// mapperProxy
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
    	// 如果为Object的方法直接调用
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        }

        if (this.isDefaultMethod(method)) {
            return this.invokeDefaultMethod(proxy, method, args);
        }
    } catch (Throwable var5) {
        throw ExceptionUtil.unwrapThrowable(var5);
    }

    MapperMethod mapperMethod = this.cachedMapperMethod(method);
    return mapperMethod.execute(this.sqlSession, args);
    }

// mapperMethod.execute
public Object execute(SqlSession sqlSession, Object[] args) {
        Object param;
        Object result;
        switch (this.command.getType()) {
            case INSERT:
                param = this.method.convertArgsToSqlCommandParam(args);
                // 调用sqlSession的相应方法,本质是调用executor的相应方法
                result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
                break;
            case UPDATE:
                param = this.method.convertArgsToSqlCommandParam(args);
                result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
                break;
            case DELETE:
                param = this.method.convertArgsToSqlCommandParam(args);
                result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
                break;
            case SELECT:
                if (this.method.returnsVoid() && this.method.hasResultHandler()) {
                    this.executeWithResultHandler(sqlSession, args);
                    result = null;
                } else if (this.method.returnsMany()) {
                    result = this.executeForMany(sqlSession, args);
                } else if (this.method.returnsMap()) {
                    result = this.executeForMap(sqlSession, args);
                } else if (this.method.returnsCursor()) {
                    result = this.executeForCursor(sqlSession, args);
                } else {
                    param = this.method.convertArgsToSqlCommandParam(args);
                    result = sqlSession.selectOne(this.command.getName(), param);
                }
                break;
            case FLUSH:
                result = sqlSession.flushStatements();
                break;
            default:
                throw new BindingException("Unknown execution method for: " + this.command.getName());
        }

测试代理demo

@Test
public void testCreateProxy() throws IOException{
    MapperProxy<UserMapper> mapperProxy = new MapperProxy<>(sqlSession, UserMapper.class, new HashMap<>());
    UserMapper mapper = (UserMapper)Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{UserMapper.class}, mapperProxy);
    int rows = mapper.insert(new User(null, "HAHAHHA", 20));
    sqlSession.commit();
}

八、插件
Mybatis允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,Mybatis允许使用插件来拦截的方法调用包括以下内容:

  • Executor --> (update,query,flushStatements,commit,rollback,getTransaction,close,isClosed)
  • ParameterHandler --> (getParameterObject,setParameters)
  • ResultSetHandler --> (handleResultSets,handleOutputParameters)
  • StatementHandler --> (prepare,parameterize,batch,update,query)

这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看Mybatis发行包中的源代码。如果你想做的不仅仅是监控方法的调用,那么你最好相当了解要重写的方法的行为。因为在试图修改或重写已有方法的行为时,很可能会破坏MyBatis的核心模块。这些都是更底层的类和方法,所以使用插件时要特别当心。
使用Mybatis插件可以极大提升Mybatis的可扩展性,如实现自动分页,分库分表,数据脱敏,sql信息统计,数据加解密等功能。
通过Mybatis提供的强大机制,使用插件是非常简单的,只需实现Interceptor接口,并指定想要拦截的方法签名即可。其中@Signature注解包含三个元素:type,method,args。其中,type指明要拦截的类,method指明方法名,args指明方法的参数列表。通过指定着三个元素,我们就能完全确定一个要拦截的方法。

package cn.j3code.studyspring.mybatis.helloworld.interceptors;


import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;

import java.sql.Statement;
import java.util.Properties;

@Slf4j
@Intercepts({
        @Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}),
        @Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),
        @Signature(type = StatementHandler.class, method = "batch", args = {Statement.class}),

})
public class SqlCostTimeInterceptor implements Interceptor {

    private Properties properties;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        // 1、拿到sql语句
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        BoundSql boundSql = statementHandler.getBoundSql();
        String sql = boundSql.getSql();
        Long start = System.currentTimeMillis();
        Object result = null;
        try{
           result = invocation.proceed();
        }finally {
            if(log.isDebugEnabled()){
                log.info("name --> {}", properties.get("name"));
                log.debug("sql语句[{}],执行耗时{}毫秒", sql, System.currentTimeMillis() - start);
            }
        }

        return result;
    }

    @Override
    public Object plugin(Object target) {
        //如果没有特殊定制,直接使用Plugin这个工具类返回一个代理对象即可
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        this.properties = properties;
    }
}

 <plugins>
        <plugin interceptor="cn.j3code.studyspring.mybatis.helloworld.interceptors.SqlCostTimeInterceptor">
            <property name="name" value="hello plugin"/>
        </plugin>
    </plugins>

插件源码流程:
Mybatis的插件很巧妙的使用代理的方式实现了责任链设计模式,插件被应用是统一调用了interceptorChain.pluginAll()方法。
在创建parameterHandler,ResultSetHandler,StatementHandler以及Executor的时候,都调用了pluginAll方法

public class InterceptorChain {
    private final List<Interceptor> interceptors = new ArrayList();
	
	public Object pluginAll(Object target) {
        Interceptor interceptor;
        for(Iterator var2 = this.interceptors.iterator(); var2.hasNext(); target = interceptor.plugin(target)) {
            interceptor = (Interceptor)var2.next();
        }

        return target;
    }



}

每一个拦截器都是Interceptor的具体实现,调用wrap方法会对目标对象进行包装,如下:

Plugin.wrap(target, this);
public class Plugin implements InvocationHandler {
	
	// 方法通过wrap进行动态代理,返回代理对象
	public static Object wrap(Object target, Interceptor interceptor) {
        Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
        Class<?> type = target.getClass();
        Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
        return interfaces.length > 0 ? Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)) : target;
    }
	
	// 本质在方法调用时会执行invoke
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
        	// 获取拦截的方法
            Set<Method> methods = (Set)this.signatureMap.get(method.getDeclaringClass());
            // 如果是被拦截的方法,本质会执行插件的intercept方法
            return methods != null && methods.contains(method) ? this.interceptor.intercept(new Invocation(this.target, method, args)) : method.invoke(this.target, args);
        } catch (Exception var5) {
            throw ExceptionUtil.unwrapThrowable(var5);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值