zoukankan      html  css  js  c++  java
  • Mybatis源码分析:BaseBuilder

    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 [com.mysql.jdbc.JDBC4Connection@42d8062c]
    ==>  Preparing: select id,name,age from student 
    ==> Parameters: 
    <==    Columns: id, name, age
    <==        Row: 1, zhangsan, 22
    <==      Total: 1

    参照文档

  • 相关阅读:
    Python处理时间 time && datetime 模块
    破解Mysql数据库密码
    JS一定要放在Body的最底部么?
    jQuery 层次选择器
    关于jquery中html()、text()、val()的区别
    解读JSP的解析过程
    JavaScript字符串分割方法
    maven install与maven package 的区别
    JSP起源、JSP的运行原理、JSP的执行过程
    Chrome隐身模式有什么用
  • 原文地址:https://www.cnblogs.com/zhengzuozhanglina/p/11291553.html
Copyright © 2011-2022 走看看