Mybatis源码分析:BaseBuilder

*/

.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
background: #F0F0F0;
}

/* Base color: saturation 0; */

.hljs,
.hljs-subst {
color: #444;
}

.hljs-comment {
color: #888888;
}

.hljs-keyword,
.hljs-attribute,
.hljs-selector-tag,
.hljs-meta-keyword,
.hljs-doctag,
.hljs-name {
font-weight: bold;
}

/* User color: hue: 0 */

.hljs-type,
.hljs-string,
.hljs-number,
.hljs-selector-id,
.hljs-selector-class,
.hljs-quote,
.hljs-template-tag,
.hljs-deletion {
color: #880000;
}

.hljs-title,
.hljs-section {
color: #880000;
font-weight: bold;
}

.hljs-regexp,
.hljs-symbol,
.hljs-variable,
.hljs-template-variable,
.hljs-link,
.hljs-selector-attr,
.hljs-selector-pseudo {
color: #BC6060;
}

/* Language color: hue: 90; */

.hljs-literal {
color: #78A960;
}

.hljs-built_in,
.hljs-bullet,
.hljs-code,
.hljs-addition {
color: #397300;
}

/* Meta color: hue: 200 */

.hljs-meta {
color: #1f7199;
}

.hljs-meta-string {
color: #4d99bf;
}

/* Misc effects */

.hljs-emphasis {
font-style: italic;
}

.hljs-strong {
font-weight: bold;
}
-->

BaseBuilder源码分析

BaseBuilder是mybatis配置文件,Mapper文件等解析器的基类,该类为抽象类,但并未声明相关的抽象方法,所有子类都可以使用BaseBuilder所提供的属性字段和实现方法,BaseBuilder下的每个子类在使用构造器实例化时,必定会调用BaseBuilder的构造方法,这是因为BaseBuilder内部维护着三个属性Configuration,TypeAliasRegistry,TypeHandlerRegistry。Configuration本身是一个"大杂烩",内部维护着各种各样的对象实例。TypeAliasRegistry,TypeHandlerRegistry就是其中两个,事实上,BaseBuilder的这两个实例就是从Configuration获取的,为什么要这样做呢?在编写mybatis配置文件时,有时候需要自定义参数别名和类型处理器,在解析配置文件的过程中,必须将自定义的别名处理器重新放入Configuration中。

属性及构造器代码段

   //配置类
  protected final Configuration configuration;
  //类型别名注册器
  protected final TypeAliasRegistry typeAliasRegistry;
  //类型处理器注册器
  protected final TypeHandlerRegistry typeHandlerRegistry;

  public BaseBuilder(Configuration configuration) {
    this.configuration = configuration;
    //获取类型别名注册器
    this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
    //获取类型处理注册器
    this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
  }

BaseBuilder提供了较多的基础方法,下面列出了一些基础的方法(代码过于简单,省略)和较为重要的方法。

方法名 参数名 作用
parseExpression regex,defaultValue 获取正则表达式对象,如果regex表达式为null,则使用默认值defaultValue作为表达式
booleanValueOf value,defaultValue 获取value的boolean值,如果为null,则使用默认值defaultValue
integerValueOf value,defaultValue 获取整形value值,如果为null,则使用默认值defaultValue
stringSetValueOf value,defaultValue 获取value的值,按照‘,’分割为数组并转为hashset,如果value为null,则使用默认值defaultValue
resolveJdbcType alias 根据别名查询对应的JDBC数据类型,JdbcType是mybatis对java.sql.Types的一次包装,并且是个枚举类,详细的信息可以查看org.apache.ibatis.type.JdbcType
resolveResultSetType alias 根据别名获取对应的结果集,详细信息参照org.apache.ibatis.mapping.ResultSetType。该类是对java.sql.ResultSet的包装,java.sql.ResultSet提供了三个值。
ResultSet.TYPE_FORWORD_ONLY 结果集的游标只能向下滚动。
ResultSet.TYPE_SCROLL_INSENSITIVE 结果集的游标可以上下移动,当数据库变化时,当前结果集不变。
ResultSet.TYPE_SCROLL_SENSITIVE 返回可滚动的结果集,当数据库变化时,当前结果集同步改变。
resolveParameterMode alias 根据别名获取ParameterMode类型,可选值为IN, OUT, INOUT,详细信息可参照org.apache.ibatis.mapping.ParameterMode类

BaseBuilder有两个比较重要的方法createInstance(String alias) 根据别名实例化对象,resolveTypeHandler(Class<?> javaType, String typeHandlerAlias) 根据类型处理器别名和java类解析出类型处理器,首先看createInstance方法。

createInstance方法:用于根据别名创建实例对象,如果别名为null,那么直接返回null对象,否则根据别名所找到的类进行实例化,在下列代码中,根据调用链发现是BaseBuilder下的 typeAliasRegistry调用了resolveAlias()方法。
前面说过typeAliasRegistry是直接从Configuration对象中获取的,那么Configuration会直接初始化一个TypeAliasRegistry对象,查找org.apache.ibatis.type.TypeAliasRegistry的构造器,可以发现注册了大量的基础数据类型,再看看 typeAliasRegistry.resolveAlias()方法。该方法首先根据别名去已注册的别名集合TYPE_ALIASES中查找对应的类,如果不存在,则使用Resources.classForName(string)获取对应的类,现在只需要知道Resources.classForName的效果跟Class.forname()的效果一样,用于根据类路径返回类对象即可。后续会分析Resources对象。

 1   protected Object createInstance(String alias) {
 2     Class<?> clazz = resolveClass(alias);
 3     if (clazz == null) {
 4       return null;
 5     }
 6     try {
 7       return resolveClass(alias).newInstance();
 8     } catch (Exception e) {
 9       throw new BuilderException("Error creating instance. Cause: " + e, e);
10     }
11   }
12
13   protected Class<?> resolveClass(String alias) {
14     if (alias == null) {
15       return null;
16     }
17     try {
18       return resolveAlias(alias);
19     } catch (Exception e) {
20       throw new BuilderException("Error resolving class. Cause: " + e, e);
21     }
22   }
23   protected Class<?> resolveAlias(String alias) {
24         return typeAliasRegistry.resolveAlias(alias);
25       }

resolveAlias()方法

 1  public <T> Class<T> resolveAlias(String string) {
 2     try {
 3       if (string == null) {
 4         return null;
 5       }
 6       // issue #748
 7       String key = string.toLowerCase(Locale.ENGLISH);
 8       Class<T> value;
 9       if (TYPE_ALIASES.containsKey(key)) {
10         value = (Class<T>) TYPE_ALIASES.get(key);
11       } else {
12         value = (Class<T>) Resources.classForName(string);
13       }
14       return value;
15     } catch (ClassNotFoundException e) {
16       throw new TypeException("Could not resolve type alias ‘" + string + "‘.  Cause: " + e, e);
17     }
18   }

resolveTypeHandler:用于根据类型别名创建对应的类型处理器,跟别名处理器类似,首先根据别名类型是否为null进行判断,再去别名注册器中寻找别名对应的类类型,使用类类型在类型处理注册器中寻找类类型对应的实例,如果不存在该实例,则采用反射对其进行实例化。下面是创建类型处理器的流程分析。可以看到,无论是什么类型,想要使用别名,必须向别名注册器中进行注册以获的<别名,类类型>对应关系。如果是类类型,还需向类型处理别名注册器中注册一个<类类型,处理器实例>的映射关系。

  • 判断类型处理器别名是否为null,如果为null值,则直接返回null对象,否则在别名注册器中寻找已经注册的类类型
  • 如果找到的类类型不为null并且不属于TypeHandler类型,则抛出BuilderException异常
  • 将寻找到的类类型强转为TypeHandler类型,在类型处理器中根据类类型寻找已经注册的类型处理器实例
  • 如果无法找寻到对应的类型处理器实例,则采用反射进行实例化。

实战演练,使用别名

该案例查询学生表student下的三个字段 id,name,age。在java中定义一个studentDto持久性对象,声明一个StudentMapper接口,该接口下有一个getStudentsByAlias()方法,表示按别名进行查询。以下是具体代码实现。

  • 声明持久类对象StudentDto
package com.zzz.mybatis.dto;

public class StudentDto {
    private Integer id;
    private String  name;
    private  Integer age;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
}
  • 声明StudentMapper接口
@Mapper
public interface StudentMapper extends SqlMapper {

    public List<StudentDto> getStudentsByAlias();
}
  • mybatis相关配置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "./mybatis-3-config.dtd">

  <configuration>
      <!--引入外部资源  -->
      <properties resource="./mybatis-mysql.properties"></properties>
    <settings>
      <!--see org.apache.ibatis.builder.xml.XMLConfigBuilder.settingsElement() -->
    <!-- 配置虚拟文件系统 -->
      <setting name="vfsImpl" value="org.apache.ibatis.io.JBoss6VFS,org.apache.ibatis.io.DefaultVFS"/>
      <!--AutoMappingBehavior NONE PARTIAL FULL  -->
      <setting name="autoMappingBehavior" value="PARTIAL"/>
      <!--AutoMappingUnknownColumnBehavior NONE WARNING FAILING  -->
      <setting name="autoMappingUnknownColumnBehavior" value="NONE"/>
      <setting name="cacheEnabled" value="true"/>
      <!--proxyFactory  -->
      <setting name="lazyLoadingEnabled" value="false"/>
      <setting name="aggressiveLazyLoading" value="false"/>
      <setting name="multipleResultSetsEnabled" value="true"/>
      <setting name="useColumnLabel" value="true"/>
      <setting name="useGeneratedKeys" value="false"/>
      <!--  SIMPLE, REUSE, BATCH-->
      <setting name="defaultExecutorType" value="SIMPLE"/>
      <setting name="defaultStatementTimeout" value="60000"/>
      <setting name="defaultFetchSize" value="0"/>
      <setting name="mapUnderscoreToCamelCase" value="false"/>
      <setting name="safeRowBoundsEnabled" value="false"/>
      <!--SESSION,STATEMENT  -->
      <setting name="localCacheScope" value="SESSION"/>
      <setting name="jdbcTypeForNull" value="OTHER"/>
      <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
      <setting name="safeResultHandlerEnabled" value="true"/>
      <setting name="defaultScriptingLanguage" value="org.apache.ibatis.scripting.xmltags.XMLLanguageDriver"/>
      <setting name="defaultEnumTypeHandler" value="org.apache.ibatis.type.EnumTypeHandler"/>
      <setting name="logPrefix" value="mybatis"/>
      <setting name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"/>
      <!-- <setting name="configurationFactory" value=""/> -->
  </settings>
      <typeAliases>
          <typeAlias  alias="student" type="com.zzz.mybatis.dto.StudentDto"/>
      </typeAliases>
      <objectFactory type="com.zzz.mybatis.object.MyObjectFacotry">
          <property name="env" value="test"/>
      </objectFactory>
      <!--设置默认的环境为开发环境  -->
        <environments default="test">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="UNPOOLED">
        <property name="driver" value="${dev.driver}"/>
        <property name="url" value="${dev.url}"/>
        <property name="username" value="${dev.username}"/>
        <property name="password" value="${dev.password}"/>
      </dataSource>
    </environment>
    <!--测试环境用  -->
    <environment id="test">
        <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="${test.driver}"/>
        <property name="url" value="${test.url}"/>
        <property name="username" value="${test.username}"/>
        <property name="password" value="${test.password}"/>
      </dataSource>
    </environment>
    <!--生产环境用  -->
    <environment id="prod">
        <transactionManager type="MANAGED"/>
      <dataSource type="JDBC">
        <property name="driver" value="${prod.driver}"/>
        <property name="url" value="${prod.url}"/>
        <property name="username" value="${prod.username}"/>
        <property name="password" value="${prod.password}"/>
      </dataSource>
    </environment>
  </environments>
   <mappers>
      <!-- <package name="com.zzz.mybatis.mapper"/> -->
      <mapper resource="./Student-Mapper.xml"/>
  </mappers>
  </configuration>
  • Student-Mapper.xml相关配置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "./mybatis-3-mapper.dtd">
<mapper namespace="com.zzz.mybatis.mapper.StudentMapper">

<cache-ref namespace="com.zzz.mybatis.mapper.StudentMapper"/>
<cache type="PERPETUAL" eviction="LRU" flushInterval="60000" readOnly="true" size="512" blocking="false">
    <property name="name" value="zhangsan"/>
</cache>

    <select id="getStudentsByAlias" resultType="student" useCache="false" >
        select id,name,age from student
    </select>
</mapper>
  • 测试类
package com.zzz.mybatis.service;

import java.util.List;
import org.apache.ibatis.session.SqlSessionManager;
import org.junit.Test;
import com.zzz.mybatis.dto.StudentDto;
import com.zzz.mybatis.mapper.StudentMapper;

public class TypeAliasTest {
    @Test
    public void typeAliasTest() {
        SqlSessionManager manager=SqlSessionManager.newInstance(getClass().getResourceAsStream("/mybatis-config.xml"));
        manager.startManagedSession();
        StudentMapper mapper=manager.getMapper(StudentMapper.class);
        List<StudentDto> rs=mapper.getStudentsByAlias();
        System.out.println(rs.toString());
    }
}
  • 测试结果
Created connection 1121453612.
Setting autocommit to false on JDBC Connection [[email protected]]
==>  Preparing: select id,name,age from student
==> Parameters:
<==    Columns: id, name, age
<==        Row: 1, zhangsan, 22
<==      Total: 1

参照文档

原文地址:https://www.cnblogs.com/zhengzuozhanglina/p/11291553.html

时间: 2024-10-15 15:08:58

Mybatis源码分析:BaseBuilder的相关文章

MyBatis 源码分析 - 配置文件解析过程

* 本文速览 由于本篇文章篇幅比较大,所以这里拿出一节对本文进行快速概括.本篇文章对 MyBatis 配置文件中常用配置的解析过程进行了较为详细的介绍和分析,包括但不限于settings,typeAliases和typeHandlers等,本文的篇幅也主要在对这三个配置解析过程的分析上.下面,我们来一起看一下本篇文章的目录结构. 从目录上可以看出,2.3节.2.5节和2.8节的内容比较多.其中2.3节是关于settings配置解析过程的分析,除了对常规的 XML 解析过程分析,本节额外的分析了元

Mybatis源码分析之Cache二级缓存原理 (五)

一:Cache类的介绍 讲解缓存之前我们需要先了解一下Cache接口以及实现MyBatis定义了一个org.apache.ibatis.cache.Cache接口作为其Cache提供者的SPI(ServiceProvider Interface) ,所有的MyBatis内部的Cache缓存,都应该实现这一接口 Cache的实现类中,Cache有不同的功能,每个功能独立,互不影响,则对于不同的Cache功能,这里使用了装饰者模式实现. 看下cache的实现类,如下图: 1.FIFOCache:先进

mybatis源码分析(一)

mybatis源码分析(sqlSessionFactory生成过程) 1. mybatis框架在现在各个IT公司的使用不用多说,这几天看了mybatis的一些源码,赶紧做个笔记. 2. 看源码从一个demo引入如下: public class TestApp { private static SqlSessionFactory sqlSessionFactory; static { InputStream inputStream; String resource = "mybatis-confi

MyBatis源码分析-MyBatis初始化流程

MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录.如何新建MyBatis源码工程请点击MyBatis源码分析-IDEA新建MyBatis源码工程. MyBatis初始化的过程也就是创建Configura

mybatis源码分析之cache创建

XMLMapperBuilder.java //解析<cache /> 配置元素,创建cache对象 private void cacheElement(XNode context) throws Exception {     if (context != null) {       String type = context.getStringAttribute("type", "PERPETUAL");       Class<? exten

MyBatis源码分析-SQL语句执行的完整流程

MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录.如何新建MyBatis源码工程请点击MyBatis源码分析-IDEA新建MyBatis源码工程. MyBatis框架主要完成的是以下2件事情: 根据JD

【MyBatis源码分析】环境准备

前言 之前一段时间写了[Spring源码分析]系列的文章,感觉对Spring的原理及使用各方面都掌握了不少,趁热打铁,开始下一个系列的文章[MyBatis源码分析],在[MyBatis源码分析]文章的基础之上,可以继续分析数据库连接池.Spring整合MyBatis源码.Spring事物管理tx等等. [MyBatis源码分析]整个文章结构相较[Spring源码分析]稍微改一改,后者会在每一部分源码分析的开头列出要分析的源码的实例,比如: 分析Bean流程加载,就会先写Bean的代码示例及xml

【MyBatis源码分析】select源码分析及小结

示例代码 之前的文章说过,对于MyBatis来说insert.update.delete是一组的,因为对于MyBatis来说它们都是update:select是一组的,因为对于MyBatis来说它就是select. 本文研究一下select的实现流程,示例代码为: 1 public void testSelectOne() { 2 System.out.println(mailDao.selectMailById(8)); 3 } selectMailById方法的实现为: 1 public M

MyBatis 源码分析 - 插件机制

1.简介 一般情况下,开源框架都会提供插件或其他形式的拓展点,供开发者自行拓展.这样的好处是显而易见的,一是增加了框架的灵活性.二是开发者可以结合实际需求,对框架进行拓展,使其能够更好的工作.以 MyBatis 为例,我们可基于 MyBatis 插件机制实现分页.分表,监控等功能.由于插件和业务无关,业务也无法感知插件的存在.因此可以无感植入插件,在无形中增强功能. 开发 MyBatis 插件需要对 MyBatis 比较深了解才行,一般来说最好能够掌握 MyBatis 的源码,门槛相对较高.本篇