Mybatis[3]

来源:互联网 发布:上海禁毒网络知识竞赛 编辑:程序博客网 时间:2024/04/28 22:35

    • 博由
    • 什么是TypeHandler
    • 自定义TypeHandler
      • 案例
        • 实现自定义时间类型处理器
        • 实体类
        • 配置typeHandler
        • 使用TypeHandler
        • 单元测试
    • 源码解析
      • 源码思路解析
        • SqlSessionFactoryjava
        • XMLConfigBuilderjava
        • DefaultSqlSessionFactoryjava
        • DefaultSqlSessionjava
        • CachingExecutorjava
          • Why CachingExecutorjava
          • CacheExecutorjava
        • SimpleExecutorjava
        • PreparedStatementHandlerjava
        • DefaultParameterHandlerjava
        • BaseTypeHandlerjava
      • 执行路径

博由

上一篇主要讲述了基本的mybatis的配置文件使用,但是没有详细针对typeHandler, plugins, objectFactory三个标签如何使用进行阐述,主要原因是因为这三个东西都是可以自定义的,其作用涉及到一些mybatis的高级功能。本文主要阐述typeHandler的使用方式。

什么是TypeHandler

    顾名思义:类型处理器;既然是处理类型的,那么处理什么类型,如何处理类型,有什么作用,才是我们需要关心的问题。
ORM:最大的问题在于使用Java实体类替换直白的SQL Table,换句话说通常一个实体类对应一个Table;也就是我们可以通过操作Class来替换操作Table的方式,就不需要关系这是Oracle的Table还是MYSQL的Table或者其他数据库的Table。那么这里最大的问题就在于javaType和jdbcType之间的映射关系,简单来说就是:实体类的类型如何与数据库的字段类型进行匹配。这样我们在更新数据时,查询数据时可以进行转换,然后直接使用实体类。
预定义映射关系:[摘自源码:TypeHandlerRegistry.java]构造初始化函数:Key:类型;value:类型处理器public TypeHandlerRegistry() {    register(Boolean.class, new BooleanTypeHandler());    register(boolean.class, new BooleanTypeHandler());    register(JdbcType.BOOLEAN, new BooleanTypeHandler());    register(JdbcType.BIT, new BooleanTypeHandler());    register(Byte.class, new ByteTypeHandler());    register(byte.class, new ByteTypeHandler());    register(JdbcType.TINYINT, new ByteTypeHandler());    register(Short.class, new ShortTypeHandler());    register(short.class, new ShortTypeHandler());    register(JdbcType.SMALLINT, new ShortTypeHandler());    register(Integer.class, new IntegerTypeHandler());    register(int.class, new IntegerTypeHandler());    register(JdbcType.INTEGER, new IntegerTypeHandler());    register(Long.class, new LongTypeHandler());    register(long.class, new LongTypeHandler());    register(Float.class, new FloatTypeHandler());    register(float.class, new FloatTypeHandler());    register(JdbcType.FLOAT, new FloatTypeHandler());    register(Double.class, new DoubleTypeHandler());    register(double.class, new DoubleTypeHandler());    register(JdbcType.DOUBLE, new DoubleTypeHandler());    register(Reader.class, new ClobReaderTypeHandler());    register(String.class, new StringTypeHandler());    register(String.class, JdbcType.CHAR, new StringTypeHandler());    register(String.class, JdbcType.CLOB, new ClobTypeHandler());    register(String.class, JdbcType.VARCHAR, new StringTypeHandler());    register(String.class, JdbcType.LONGVARCHAR, new ClobTypeHandler());    register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());    register(String.class, JdbcType.NCHAR, new NStringTypeHandler());    register(String.class, JdbcType.NCLOB, new NClobTypeHandler());    register(JdbcType.CHAR, new StringTypeHandler());    register(JdbcType.VARCHAR, new StringTypeHandler());    register(JdbcType.CLOB, new ClobTypeHandler());    register(JdbcType.LONGVARCHAR, new ClobTypeHandler());    register(JdbcType.NVARCHAR, new NStringTypeHandler());    register(JdbcType.NCHAR, new NStringTypeHandler());    register(JdbcType.NCLOB, new NClobTypeHandler());    register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());    register(JdbcType.ARRAY, new ArrayTypeHandler());    register(BigInteger.class, new BigIntegerTypeHandler());    register(JdbcType.BIGINT, new LongTypeHandler());    register(BigDecimal.class, new BigDecimalTypeHandler());    register(JdbcType.REAL, new BigDecimalTypeHandler());    register(JdbcType.DECIMAL, new BigDecimalTypeHandler());    register(JdbcType.NUMERIC, new BigDecimalTypeHandler());    register(InputStream.class, new BlobInputStreamTypeHandler());    register(Byte[].class, new ByteObjectArrayTypeHandler());    register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());    register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());    register(byte[].class, new ByteArrayTypeHandler());    register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());    register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());    register(JdbcType.LONGVARBINARY, new BlobTypeHandler());    register(JdbcType.BLOB, new BlobTypeHandler());    register(Object.class, UNKNOWN_TYPE_HANDLER);    register(Object.class, JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);    register(JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);    register(Date.class, new DateTypeHandler());    register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());    register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());    register(JdbcType.TIMESTAMP, new DateTypeHandler());    register(JdbcType.DATE, new DateOnlyTypeHandler());    register(JdbcType.TIME, new TimeOnlyTypeHandler());    register(java.sql.Date.class, new SqlDateTypeHandler());    register(java.sql.Time.class, new SqlTimeTypeHandler());    register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());    // mybatis-typehandlers-jsr310    try {      // since 1.0.0      register("java.time.Instant", "org.apache.ibatis.type.InstantTypeHandler");      register("java.time.LocalDateTime", "org.apache.ibatis.type.LocalDateTimeTypeHandler");      register("java.time.LocalDate", "org.apache.ibatis.type.LocalDateTypeHandler");      register("java.time.LocalTime", "org.apache.ibatis.type.LocalTimeTypeHandler");      register("java.time.OffsetDateTime", "org.apache.ibatis.type.OffsetDateTimeTypeHandler");      register("java.time.OffsetTime", "org.apache.ibatis.type.OffsetTimeTypeHandler");      register("java.time.ZonedDateTime", "org.apache.ibatis.type.ZonedDateTimeTypeHandler");      // since 1.0.1      register("java.time.Month", "org.apache.ibatis.type.MonthTypeHandler");      register("java.time.Year", "org.apache.ibatis.type.YearTypeHandler");      // since 1.0.2      register("java.time.YearMonth", "org.apache.ibatis.type.YearMonthTypeHandler");      register("java.time.chrono.JapaneseDate", "org.apache.ibatis.type.JapaneseDateTypeHandler");    } catch (ClassNotFoundException e) {      // no JSR-310 handlers    }    // issue #273    register(Character.class, new CharacterTypeHandler());    register(char.class, new CharacterTypeHandler())

思考:

默认情况下,mybatis会帮助我们完成一般类型(预定义类型)的转化过程,那么在某些情况下,需要一些特殊类型的处理,就没办法满足了,这个时候,就需要自定义类型处理器,并设定在特定场合触发类型转换。

自定义TypeHandler

我们描述了typeHandler的产生背景,接下来我们需要重点阐述的是如何使用和深度理解其内部机制。

案例

数据库表:CREATE TABLE `user1` (  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',  `name` varchar(128) NOT NULL DEFAULT '' COMMENT '名称',  `created_at` varchar(10) NOT NULL DEFAULT '' COMMENT '创建时间',  `updated_at` varchar(10) NOT NULL DEFAULT '' COMMENT '更新时间',  PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='用户表 - 测试typeHandler';满足:插入时写入Date数据,存储为字符串时间戳,查询获取时为Date类型;

实现自定义时间类型处理器

package com.typehandler.handlers;import org.apache.ibatis.type.BaseTypeHandler;import org.apache.ibatis.type.JdbcType;import org.apache.ibatis.type.MappedJdbcTypes;import org.apache.ibatis.type.MappedTypes;import java.sql.CallableStatement;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Date;/** * Created by wangzhiping on 17/3/1. */// 设置映射的数据库类型,必须是JdbcType指定类型@MappedJdbcTypes(JdbcType.VARCHAR)// 设置映射的java类型,主要是java的类型@MappedTypes(value=java.util.Date.class)// 如果没有在javacode中指定javaType 和 javaType那么需要配置文件指定,如果制定了可以不需要再制定public class MyDateTypeHandler extends BaseTypeHandler<Date>{  // 继承BaseTypeHandler,期泛型就是需要转化的类型    /**     * 该函数:     * @param ps jdbc中的PreparedStatement对象     * @param i 数据的位置索引     * @param parameter 需要插入的数据参数(一般是执行更新、删除、插入时调用)     * @param jdbcType 数据库类型     * @throws SQLException     */    @Override    public void setNonNullParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType) throws SQLException {        // 注入值时        Long timestamp = parameter.getTime() / 1000;        ps.setString(i, timestamp.toString());    }    @Override    public Date getNullableResult(ResultSet rs, String columnName) throws SQLException {        // 从数据库中获取数据        String timestamp = rs.getString(columnName);        return new Date(Long.parseLong(timestamp) * 1000);    }    @Override    public Date getNullableResult(ResultSet rs, int columnIndex) throws SQLException {        String timestamp = rs.getString(columnIndex);        return new Date(Long.parseLong(timestamp) * 1000);    }    @Override    public Date getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {        String timestamp = cs.getString(columnIndex);        return new Date(Long.parseLong(timestamp) * 1000);    }}

实体类

    package com.typehandler.pojo;import org.apache.ibatis.type.Alias;import java.io.Serializable;import java.util.Date;/** * Created by wangzhiping on 17/2/21. */public class User1 implements Serializable{    private Integer id;    private String name;    private Date createdAt;    private Date updatedAt;    public User1() {    }    public User1(Integer id, String name, Date createdAt, Date updatedAt) {        this.id = id;        this.name = name;        this.createdAt = createdAt;        this.updatedAt = updatedAt;    }    public int getId() {        return id;    }    public void setId(int id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public Date getCreatedAt() {        return createdAt;    }    public void setCreatedAt(Date createdAt) {        this.createdAt = createdAt;    }    public Date getUpdatedAt() {        return updatedAt;    }    public void setUpdatedAt(Date updatedAt) {        this.updatedAt = updatedAt;    }    @Override    public String toString() {        return "User{" +                "id=" + id +                ", name='" + name + '\'' +                ", createdAt=" + createdAt +                ", updatedAt=" + updatedAt +                '}';    }}

配置typeHandler

<!-- 配置typehandlers -->    <typeHandlers>        <package name="com.typehandler.handlers" />        <!-- <typeHandler handler="com.typehandler.handlers.MyDateTypeHandler" /> -->    </typeHandlers> 其中package和typeHandler的概念与typeAliases中的package和typeAlias一致。

使用TypeHandler

<?xml version="1.0" encoding="utf-8" ?><!DOCTYPE mapper        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"        "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.typehandler.pojo.User1">    <insert id="add" parameterType="User1">        INSERT INTO user1        VALUES(null, #{name},        #{createdAt, javaType=java.util.Date, jdbcType=VARCHAR, typeHandler=com.typehandler.handlers.MyDateTypeHandler},        #{createdAt, javaType=java.util.Date, jdbcType=VARCHAR, typeHandler=com.typehandler.handlers.MyDateTypeHandler})    </insert>    <resultMap id="user1Map" type="User1">        <id property="id" column="id"/>        <result property="name" column="name"/>        <!-- 需要做转换的字段必须指定typeHandler -->        <result property="createdAt" column="created_at" typeHandler="com.typehandler.handlers.MyDateTypeHandler"/>        <result property="updatedAt" column="updated_at" typeHandler="com.typehandler.handlers.MyDateTypeHandler"/>    </resultMap>    <select id="findById" parameterType="int" resultMap="user1Map">        SELECT *        FROM user1        WHERE id=#{id}    </select></mapper>
有两种使用方式:1、在resultMap中使用:result子标签的typeHandler指定类型转化类,如果自定义的类型转化类明确说明了JavaType和JdbcType,那么这里可以不用指定,否则需要指定转换匹配类型情况;2、在sql语法中使用,例如:#{field, javaType=,jdbcType=,typeHandler=} 类似这种格式进行使用;

单元测试

package com.typehandler;import com.typehandler.pojo.User1;import com.typehandler.utils.MybatisUtils;import org.apache.ibatis.session.SqlSession;import org.junit.Test;import java.util.Date;/** * Created by wangzhiping on 17/3/1. */public class TypeHandlerTest {    private MybatisUtils instance = MybatisUtils.getInstance("mybatis-config.xml");    @Test    public void testWirteDateTypeHandler(){        SqlSession session = instance.getSession();        try{            session.insert("com.typehandler.pojo.User1.add", new User1(null, "test", new Date(), new Date()));            session.commit();        } catch (Exception e){            session.rollback();            e.printStackTrace();        } finally {          instance.closeSession(session);        }    }    @Test    public void testReadDateTypeHandler(){        SqlSession session = instance.getSession();        try{            User1 user1 = session.selectOne("com.typehandler.pojo.User1.findById", 1);            System.out.println(user1);        } catch (Exception e){            e.printStackTrace();        } finally {            instance.closeSession(session);        }    }}

源码解析

源码思路解析

SqlSessionFactory.java

/** *    Copyright 2009-2016 the original author or authors. * *    Licensed under the Apache License, Version 2.0 (the "License"); *    you may not use this file except in compliance with the License. *    You may obtain a copy of the License at * *       http://www.apache.org/licenses/LICENSE-2.0 * *    Unless required by applicable law or agreed to in writing, software *    distributed under the License is distributed on an "AS IS" BASIS, *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *    See the License for the specific language governing permissions and *    limitations under the License. */package org.apache.ibatis.session;import java.io.IOException;import java.io.InputStream;import java.io.Reader;import java.util.Properties;import org.apache.ibatis.builder.xml.XMLConfigBuilder;import org.apache.ibatis.exceptions.ExceptionFactory;import org.apache.ibatis.executor.ErrorContext;import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;/** * Builds {@link SqlSession} instances. * * @author Clinton Begin */public class SqlSessionFactoryBuilder {  public SqlSessionFactory build(Reader reader) {    return build(reader, null, null);  }  public SqlSessionFactory build(Reader reader, String environment) {    return build(reader, environment, null);  }  public SqlSessionFactory build(Reader reader, Properties properties) {    return build(reader, null, properties);  }  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {    try {      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);      return build(parser.parse());    } catch (Exception e) {      throw ExceptionFactory.wrapException("Error building SqlSession.", e);    } finally {      ErrorContext.instance().reset();      try {        reader.close();      } catch (IOException e) {        // Intentionally ignore. Prefer previous error.      }    }  }  public SqlSessionFactory build(InputStream inputStream) {    return build(inputStream, null, null);  }  public SqlSessionFactory build(InputStream inputStream, String environment) {    return build(inputStream, environment, null);  }  public SqlSessionFactory build(InputStream inputStream, Properties properties) {    return build(inputStream, null, properties);  }  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {    try {      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);      // 解析配置文件构建Configuration对象      return build(parser.parse());    } catch (Exception e) {      throw ExceptionFactory.wrapException("Error building SqlSession.", e);    } finally {      ErrorContext.instance().reset();      try {        inputStream.close();      } catch (IOException e) {        // Intentionally ignore. Prefer previous error.      }    }  }  public SqlSessionFactory build(Configuration config) {    // 构建内置缺省的SqlSessionFactory对象    return new DefaultSqlSessionFactory(config);  }}

XMLConfigBuilder.java

解析加载配置文件/** *    Copyright 2009-2016 the original author or authors. * *    Licensed under the Apache License, Version 2.0 (the "License"); *    you may not use this file except in compliance with the License. *    You may obtain a copy of the License at * *       http://www.apache.org/licenses/LICENSE-2.0 * *    Unless required by applicable law or agreed to in writing, software *    distributed under the License is distributed on an "AS IS" BASIS, *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *    See the License for the specific language governing permissions and *    limitations under the License. */package org.apache.ibatis.builder.xml;import java.io.InputStream;import java.io.Reader;import java.util.Properties;import javax.sql.DataSource;import org.apache.ibatis.builder.BaseBuilder;import org.apache.ibatis.builder.BuilderException;import org.apache.ibatis.datasource.DataSourceFactory;import org.apache.ibatis.executor.ErrorContext;import org.apache.ibatis.executor.loader.ProxyFactory;import org.apache.ibatis.io.Resources;import org.apache.ibatis.io.VFS;import org.apache.ibatis.logging.Log;import org.apache.ibatis.mapping.DatabaseIdProvider;import org.apache.ibatis.mapping.Environment;import org.apache.ibatis.parsing.XNode;import org.apache.ibatis.parsing.XPathParser;import org.apache.ibatis.plugin.Interceptor;import org.apache.ibatis.reflection.DefaultReflectorFactory;import org.apache.ibatis.reflection.MetaClass;import org.apache.ibatis.reflection.ReflectorFactory;import org.apache.ibatis.reflection.factory.ObjectFactory;import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;import org.apache.ibatis.session.AutoMappingBehavior;import org.apache.ibatis.session.AutoMappingUnknownColumnBehavior;import org.apache.ibatis.session.Configuration;import org.apache.ibatis.session.ExecutorType;import org.apache.ibatis.session.LocalCacheScope;import org.apache.ibatis.transaction.TransactionFactory;import org.apache.ibatis.type.JdbcType;/** * @author Clinton Begin * @author Kazuki Shimizu */public class XMLConfigBuilder extends BaseBuilder {  private boolean parsed;  private XPathParser parser;  private String environment;  private ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();  public XMLConfigBuilder(Reader reader) {    this(reader, null, null);  }  public XMLConfigBuilder(Reader reader, String environment) {    this(reader, environment, null);  }  public XMLConfigBuilder(Reader reader, String environment, Properties props) {    this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);  }  public XMLConfigBuilder(InputStream inputStream) {    this(inputStream, null, null);  }  public XMLConfigBuilder(InputStream inputStream, String environment) {    this(inputStream, environment, null);  }  public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);  }  private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {    super(new Configuration());    ErrorContext.instance().resource("SQL Mapper Configuration");    this.configuration.setVariables(props);    this.parsed = false;    this.environment = environment;    this.parser = parser;  }  public Configuration parse() {    if (parsed) {      throw new BuilderException("Each XMLConfigBuilder can only be used once.");    }    parsed = true;    parseConfiguration(parser.evalNode("/configuration"));    return configuration;  }  // 解析xml配置文件  // 每个标签的解析都有,然后会加载到Configuration对象中  private void parseConfiguration(XNode root) {    try {      //issue #117 read properties first      propertiesElement(root.evalNode("properties"));      Properties settings = settingsAsProperties(root.evalNode("settings"));      loadCustomVfs(settings);      typeAliasesElement(root.evalNode("typeAliases"));      pluginElement(root.evalNode("plugins"));      objectFactoryElement(root.evalNode("objectFactory"));      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));      reflectorFactoryElement(root.evalNode("reflectorFactory"));      settingsElement(settings);      // read it after objectFactory and objectWrapperFactory issue #631      environmentsElement(root.evalNode("environments"));      databaseIdProviderElement(root.evalNode("databaseIdProvider"));      typeHandlerElement(root.evalNode("typeHandlers"));      mapperElement(root.evalNode("mappers"));    } catch (Exception e) {      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);    }  }  private Properties settingsAsProperties(XNode context) {    if (context == null) {      return new Properties();    }    Properties props = context.getChildrenAsProperties();    // Check that all settings are known to the configuration class    MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);    for (Object key : props.keySet()) {      if (!metaConfig.hasSetter(String.valueOf(key))) {        throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");      }    }    return props;  }  private void loadCustomVfs(Properties props) throws ClassNotFoundException {    String value = props.getProperty("vfsImpl");    if (value != null) {      String[] clazzes = value.split(",");      for (String clazz : clazzes) {        if (!clazz.isEmpty()) {          @SuppressWarnings("unchecked")          Class<? extends VFS> vfsImpl = (Class<? extends VFS>)Resources.classForName(clazz);          configuration.setVfsImpl(vfsImpl);        }      }    }  }  private void typeAliasesElement(XNode parent) {    if (parent != null) {      for (XNode child : parent.getChildren()) {        if ("package".equals(child.getName())) {          String typeAliasPackage = child.getStringAttribute("name");          configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);        } else {          String alias = child.getStringAttribute("alias");          String type = child.getStringAttribute("type");          try {            Class<?> clazz = Resources.classForName(type);            if (alias == null) {              typeAliasRegistry.registerAlias(clazz);            } else {              typeAliasRegistry.registerAlias(alias, clazz);            }          } catch (ClassNotFoundException e) {            throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);          }        }      }    }  }  private void pluginElement(XNode parent) throws Exception {    if (parent != null) {      for (XNode child : parent.getChildren()) {        String interceptor = child.getStringAttribute("interceptor");        Properties properties = child.getChildrenAsProperties();        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();        interceptorInstance.setProperties(properties);        configuration.addInterceptor(interceptorInstance);      }    }  }  private void objectFactoryElement(XNode context) throws Exception {    if (context != null) {      String type = context.getStringAttribute("type");      Properties properties = context.getChildrenAsProperties();      ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance();      factory.setProperties(properties);      configuration.setObjectFactory(factory);    }  }  private void objectWrapperFactoryElement(XNode context) throws Exception {    if (context != null) {      String type = context.getStringAttribute("type");      ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).newInstance();      configuration.setObjectWrapperFactory(factory);    }  }  private void reflectorFactoryElement(XNode context) throws Exception {    if (context != null) {       String type = context.getStringAttribute("type");       ReflectorFactory factory = (ReflectorFactory) resolveClass(type).newInstance();       configuration.setReflectorFactory(factory);    }  }  private void propertiesElement(XNode context) throws Exception {    if (context != null) {      Properties defaults = context.getChildrenAsProperties();      String resource = context.getStringAttribute("resource");      String url = context.getStringAttribute("url");      if (resource != null && url != null) {        throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");      }      if (resource != null) {        defaults.putAll(Resources.getResourceAsProperties(resource));      } else if (url != null) {        defaults.putAll(Resources.getUrlAsProperties(url));      }      Properties vars = configuration.getVariables();      if (vars != null) {        defaults.putAll(vars);      }      parser.setVariables(defaults);      configuration.setVariables(defaults);    }  }  private void settingsElement(Properties props) throws Exception {    configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));    configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));    configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));    configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));    configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));    configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));    configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));    configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));    configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));    configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));    configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));    configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));    configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));    configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));    configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));    configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));    configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));    configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));    configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));    configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));    configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));    configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));    configuration.setLogPrefix(props.getProperty("logPrefix"));    @SuppressWarnings("unchecked")    Class<? extends Log> logImpl = (Class<? extends Log>)resolveClass(props.getProperty("logImpl"));    configuration.setLogImpl(logImpl);    configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));  }  private void environmentsElement(XNode context) throws Exception {    if (context != null) {      if (environment == null) {        environment = context.getStringAttribute("default");      }      for (XNode child : context.getChildren()) {        String id = child.getStringAttribute("id");        if (isSpecifiedEnvironment(id)) {          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));          DataSource dataSource = dsFactory.getDataSource();          Environment.Builder environmentBuilder = new Environment.Builder(id)              .transactionFactory(txFactory)              .dataSource(dataSource);          configuration.setEnvironment(environmentBuilder.build());        }      }    }  }  private void databaseIdProviderElement(XNode context) throws Exception {    DatabaseIdProvider databaseIdProvider = null;    if (context != null) {      String type = context.getStringAttribute("type");      // awful patch to keep backward compatibility      if ("VENDOR".equals(type)) {          type = "DB_VENDOR";      }      Properties properties = context.getChildrenAsProperties();      databaseIdProvider = (DatabaseIdProvider) resolveClass(type).newInstance();      databaseIdProvider.setProperties(properties);    }    Environment environment = configuration.getEnvironment();    if (environment != null && databaseIdProvider != null) {      String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());      configuration.setDatabaseId(databaseId);    }  }  private TransactionFactory transactionManagerElement(XNode context) throws Exception {    if (context != null) {      String type = context.getStringAttribute("type");      Properties props = context.getChildrenAsProperties();      TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();      factory.setProperties(props);      return factory;    }    throw new BuilderException("Environment declaration requires a TransactionFactory.");  }  private DataSourceFactory dataSourceElement(XNode context) throws Exception {    if (context != null) {      String type = context.getStringAttribute("type");      Properties props = context.getChildrenAsProperties();      DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();      factory.setProperties(props);      return factory;    }    throw new BuilderException("Environment declaration requires a DataSourceFactory.");  }  // 这里解析typeHandler元素  private void typeHandlerElement(XNode parent) throws Exception {    if (parent != null) {      for (XNode child : parent.getChildren()) {        // package子标签的处理方式,会默认加载package路径的所有类        if ("package".equals(child.getName())) {          String typeHandlerPackage = child.getStringAttribute("name");          typeHandlerRegistry.register(typeHandlerPackage);        } else {          // javaType          String javaTypeName = child.getStringAttribute("javaType");          // jdbcType          String jdbcTypeName = child.getStringAttribute("jdbcType");          // typeHandler Class          String handlerTypeName = child.getStringAttribute("handler");          Class<?> javaTypeClass = resolveClass(javaTypeName);          JdbcType jdbcType = resolveJdbcType(jdbcTypeName);          Class<?> typeHandlerClass = resolveClass(handlerTypeName);          // 解析后,完成注册          if (javaTypeClass != null) {            if (jdbcType == null) {              typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);            } else {              typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);            }          } else {            typeHandlerRegistry.register(typeHandlerClass);          }        }      }    }  }  private void mapperElement(XNode parent) throws Exception {    if (parent != null) {      for (XNode child : parent.getChildren()) {        if ("package".equals(child.getName())) {          String mapperPackage = child.getStringAttribute("name");          configuration.addMappers(mapperPackage);        } else {          String resource = child.getStringAttribute("resource");          String url = child.getStringAttribute("url");          String mapperClass = child.getStringAttribute("class");          if (resource != null && url == null && mapperClass == null) {            ErrorContext.instance().resource(resource);            InputStream inputStream = Resources.getResourceAsStream(resource);            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());            mapperParser.parse();          } else if (resource == null && url != null && mapperClass == null) {            ErrorContext.instance().resource(url);            InputStream inputStream = Resources.getUrlAsStream(url);            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());            mapperParser.parse();          } else if (resource == null && url == null && mapperClass != null) {            Class<?> mapperInterface = Resources.classForName(mapperClass);            configuration.addMapper(mapperInterface);          } else {            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");          }        }      }    }  }  private boolean isSpecifiedEnvironment(String id) {    if (environment == null) {      throw new BuilderException("No environment specified.");    } else if (id == null) {      throw new BuilderException("Environment requires an id attribute.");    } else if (environment.equals(id)) {      return true;    }    return false;  }}

DefaultSqlSessionFactory.java

主要目的是用来获取SqlSession对象/** *    Copyright 2009-2015 the original author or authors. * *    Licensed under the Apache License, Version 2.0 (the "License"); *    you may not use this file except in compliance with the License. *    You may obtain a copy of the License at * *       http://www.apache.org/licenses/LICENSE-2.0 * *    Unless required by applicable law or agreed to in writing, software *    distributed under the License is distributed on an "AS IS" BASIS, *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *    See the License for the specific language governing permissions and *    limitations under the License. */package org.apache.ibatis.session.defaults;import java.sql.Connection;import java.sql.SQLException;import org.apache.ibatis.exceptions.ExceptionFactory;import org.apache.ibatis.executor.ErrorContext;import org.apache.ibatis.executor.Executor;import org.apache.ibatis.mapping.Environment;import org.apache.ibatis.session.Configuration;import org.apache.ibatis.session.ExecutorType;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.TransactionIsolationLevel;import org.apache.ibatis.transaction.Transaction;import org.apache.ibatis.transaction.TransactionFactory;import org.apache.ibatis.transaction.managed.ManagedTransactionFactory;/** * @author Clinton Begin */public class DefaultSqlSessionFactory implements SqlSessionFactory {  private final Configuration configuration;  public DefaultSqlSessionFactory(Configuration configuration) {    this.configuration = configuration;  }  @Override  public SqlSession openSession() {    // 这个是我们通常使用的获取Session的方法    // 这里选择了默认的执行器(SIMPLE)ExecutorType.SIMPLE    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);  }  @Override  public SqlSession openSession(boolean autoCommit) {    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);  }  @Override  public SqlSession openSession(ExecutorType execType) {    return openSessionFromDataSource(execType, null, false);  }  @Override  public SqlSession openSession(TransactionIsolationLevel level) {    return openSessionFromDataSource(configuration.getDefaultExecutorType(), level, false);  }  @Override  public SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) {    return openSessionFromDataSource(execType, level, false);  }  @Override  public SqlSession openSession(ExecutorType execType, boolean autoCommit) {    return openSessionFromDataSource(execType, null, autoCommit);  }  @Override  public SqlSession openSession(Connection connection) {    return openSessionFromConnection(configuration.getDefaultExecutorType(), connection);  }  @Override  public SqlSession openSession(ExecutorType execType, Connection connection) {    return openSessionFromConnection(execType, connection);  }  @Override  public Configuration getConfiguration() {    return configuration;  }  //  这个函数就是获取Session的方法  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {    Transaction tx = null;    try {      // 获取环境(DB环境,通过加载dataSource和事务管理信息transactionManager)      final Environment environment = configuration.getEnvironment();      // 例如选择的是JDBC,那么事务将选择JDBC的事务处理对象      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);      // 创建执行器,用于执行SQL的全过程和策略      final Executor executor = configuration.newExecutor(tx, execType);      // 创建SqlSession对象默认的实现是 DefaultSqlSession      return new DefaultSqlSession(configuration, executor, autoCommit);    } catch (Exception e) {      closeTransaction(tx); // may have fetched a connection so lets call close()      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);    } finally {      ErrorContext.instance().reset();    }  }  private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {    try {      boolean autoCommit;      try {        autoCommit = connection.getAutoCommit();      } catch (SQLException e) {        // Failover to true, as most poor drivers        // or databases won't support transactions        autoCommit = true;      }            final Environment environment = configuration.getEnvironment();      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);      final Transaction tx = transactionFactory.newTransaction(connection);      final Executor executor = configuration.newExecutor(tx, execType);      return new DefaultSqlSession(configuration, executor, autoCommit);    } catch (Exception e) {      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);    } finally {      ErrorContext.instance().reset();    }  }  private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {    if (environment == null || environment.getTransactionFactory() == null) {      return new ManagedTransactionFactory();    }    return environment.getTransactionFactory();  }  private void closeTransaction(Transaction tx) {    if (tx != null) {      try {        tx.close();      } catch (SQLException ignore) {        // Intentionally ignore. Prefer previous error.      }    }  }}

DefaultSqlSession.java

/** *    Copyright 2009-2015 the original author or authors. * *    Licensed under the Apache License, Version 2.0 (the "License"); *    you may not use this file except in compliance with the License. *    You may obtain a copy of the License at * *       http://www.apache.org/licenses/LICENSE-2.0 * *    Unless required by applicable law or agreed to in writing, software *    distributed under the License is distributed on an "AS IS" BASIS, *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *    See the License for the specific language governing permissions and *    limitations under the License. */package org.apache.ibatis.session.defaults;import java.io.IOException;import java.sql.Connection;import java.sql.SQLException;import java.util.ArrayList;import java.util.Collection;import java.util.HashMap;import java.util.List;import java.util.Map;import org.apache.ibatis.binding.BindingException;import org.apache.ibatis.cursor.Cursor;import org.apache.ibatis.exceptions.ExceptionFactory;import org.apache.ibatis.exceptions.TooManyResultsException;import org.apache.ibatis.executor.BatchResult;import org.apache.ibatis.executor.ErrorContext;import org.apache.ibatis.executor.Executor;import org.apache.ibatis.executor.result.DefaultMapResultHandler;import org.apache.ibatis.executor.result.DefaultResultContext;import org.apache.ibatis.mapping.MappedStatement;import org.apache.ibatis.session.Configuration;import org.apache.ibatis.session.ResultHandler;import org.apache.ibatis.session.RowBounds;import org.apache.ibatis.session.SqlSession;/** * * The default implementation for {@link SqlSession}. * Note that this class is not Thread-Safe. * * @author Clinton Begin */public class DefaultSqlSession implements SqlSession {  private Configuration configuration;  private Executor executor;  private boolean autoCommit;  private boolean dirty;  private List<Cursor<?>> cursorList;  public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {    this.configuration = configuration;    this.executor = executor;    this.dirty = false;    this.autoCommit = autoCommit;  }  public DefaultSqlSession(Configuration configuration, Executor executor) {    this(configuration, executor, false);  }  @Override  public <T> T selectOne(String statement) {    return this.<T>selectOne(statement, null);  }  @Override  public <T> T selectOne(String statement, Object parameter) {    // Popular vote was to return null on 0 results and throw exception on too many.    List<T> list = this.<T>selectList(statement, parameter);    if (list.size() == 1) {      return list.get(0);    } else if (list.size() > 1) {      throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());    } else {      return null;    }  }  @Override  public <K, V> Map<K, V> selectMap(String statement, String mapKey) {    return this.selectMap(statement, null, mapKey, RowBounds.DEFAULT);  }  @Override  public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {    return this.selectMap(statement, parameter, mapKey, RowBounds.DEFAULT);  }  @Override  public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {    final List<? extends V> list = selectList(statement, parameter, rowBounds);    final DefaultMapResultHandler<K, V> mapResultHandler = new DefaultMapResultHandler<K, V>(mapKey,        configuration.getObjectFactory(), configuration.getObjectWrapperFactory(), configuration.getReflectorFactory());    final DefaultResultContext<V> context = new DefaultResultContext<V>();    for (V o : list) {      context.nextResultObject(o);      mapResultHandler.handleResult(context);    }    return mapResultHandler.getMappedResults();  }  @Override  public <T> Cursor<T> selectCursor(String statement) {    return selectCursor(statement, null);  }  @Override  public <T> Cursor<T> selectCursor(String statement, Object parameter) {    return selectCursor(statement, parameter, RowBounds.DEFAULT);  }  @Override  public <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds) {    try {      MappedStatement ms = configuration.getMappedStatement(statement);      Cursor<T> cursor = executor.queryCursor(ms, wrapCollection(parameter), rowBounds);      registerCursor(cursor);      return cursor;    } catch (Exception e) {      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);    } finally {      ErrorContext.instance().reset();    }  }  @Override  public <E> List<E> selectList(String statement) {    return this.selectList(statement, null);  }  @Override  public <E> List<E> selectList(String statement, Object parameter) {    return this.selectList(statement, parameter, RowBounds.DEFAULT);  }// 查询方法:  @Override  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {    try {      MappedStatement ms = configuration.getMappedStatement(statement);      return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);    } catch (Exception e) {      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);    } finally {      ErrorContext.instance().reset();    }  }  @Override  public void select(String statement, Object parameter, ResultHandler handler) {    select(statement, parameter, RowBounds.DEFAULT, handler);  }  @Override  public void select(String statement, ResultHandler handler) {    select(statement, null, RowBounds.DEFAULT, handler);  }  @Override  public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {    try {      MappedStatement ms = configuration.getMappedStatement(statement);      executor.query(ms, wrapCollection(parameter), rowBounds, handler);    } catch (Exception e) {      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);    } finally {      ErrorContext.instance().reset();    }  }  @Override  public int insert(String statement) {    return insert(statement, null);  }  @Override  public int insert(String statement, Object parameter) {    return update(statement, parameter);  }  @Override  public int update(String statement) {    return update(statement, null);  }  // 更新操作(insert\delete\update操作核心方法)  @Override  public int update(String statement, Object parameter) {    try {      dirty = true;      // 获取Statement      MappedStatement ms = configuration.getMappedStatement(statement);      // 通过执行器执行      return executor.update(ms, wrapCollection(parameter));    } catch (Exception e) {      throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);    } finally {      ErrorContext.instance().reset();    }  }  @Override  public int delete(String statement) {    return update(statement, null);  }  @Override  public int delete(String statement, Object parameter) {    return update(statement, parameter);  }  @Override  public void commit() {    commit(false);  }  @Override  public void commit(boolean force) {    try {      executor.commit(isCommitOrRollbackRequired(force));      dirty = false;    } catch (Exception e) {      throw ExceptionFactory.wrapException("Error committing transaction.  Cause: " + e, e);    } finally {      ErrorContext.instance().reset();    }  }  @Override  public void rollback() {    rollback(false);  }  @Override  public void rollback(boolean force) {    try {      executor.rollback(isCommitOrRollbackRequired(force));      dirty = false;    } catch (Exception e) {      throw ExceptionFactory.wrapException("Error rolling back transaction.  Cause: " + e, e);    } finally {      ErrorContext.instance().reset();    }  }  @Override  public List<BatchResult> flushStatements() {    try {      return executor.flushStatements();    } catch (Exception e) {      throw ExceptionFactory.wrapException("Error flushing statements.  Cause: " + e, e);    } finally {      ErrorContext.instance().reset();    }  }  @Override  public void close() {    try {      executor.close(isCommitOrRollbackRequired(false));      closeCursors();      dirty = false;    } finally {      ErrorContext.instance().reset();    }  }  private void closeCursors() {    if (cursorList != null && cursorList.size() != 0) {      for (Cursor<?> cursor : cursorList) {        try {          cursor.close();        } catch (IOException e) {          throw ExceptionFactory.wrapException("Error closing cursor.  Cause: " + e, e);        }      }      cursorList.clear();    }  }  @Override  public Configuration getConfiguration() {    return configuration;  }  @Override  public <T> T getMapper(Class<T> type) {    return configuration.<T>getMapper(type, this);  }  @Override  public Connection getConnection() {    try {      return executor.getTransaction().getConnection();    } catch (SQLException e) {      throw ExceptionFactory.wrapException("Error getting a new connection.  Cause: " + e, e);    }  }  @Override  public void clearCache() {    executor.clearLocalCache();  }  private <T> void registerCursor(Cursor<T> cursor) {    if (cursorList == null) {      cursorList = new ArrayList<Cursor<?>>();    }    cursorList.add(cursor);  }  private boolean isCommitOrRollbackRequired(boolean force) {    return (!autoCommit && dirty) || force;  }  private Object wrapCollection(final Object object) {    if (object instanceof Collection) {      StrictMap<Object> map = new StrictMap<Object>();      map.put("collection", object);      if (object instanceof List) {        map.put("list", object);      }      return map;    } else if (object != null && object.getClass().isArray()) {      StrictMap<Object> map = new StrictMap<Object>();      map.put("array", object);      return map;    }    return object;  }  public static class StrictMap<V> extends HashMap<String, V> {    private static final long serialVersionUID = -5741767162221585340L;    @Override    public V get(Object key) {      if (!super.containsKey(key)) {        throw new BindingException("Parameter '" + key + "' not found. Available parameters are " + this.keySet());      }      return super.get(key);    }  }}

CachingExecutor.java

Why CachingExecutor.java
    为啥是CachingExecutor执行器呢?默认是Executor.SIMPLE & cacheEnabled=true,可以查看Configuration中的newExecutor方法,这里使用的装试器模式,    public Executor newExecutor(Transaction transaction, ExecutorType executorType) {    executorType = executorType == null ? defaultExecutorType : executorType;    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;    Executor 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 (cacheEnabled) {      executor = new CachingExecutor(executor);    }    executor = (Executor) interceptorChain.pluginAll(executor);    return executor;  }
CacheExecutor.java
/** *    Copyright 2009-2015 the original author or authors. * *    Licensed under the Apache License, Version 2.0 (the "License"); *    you may not use this file except in compliance with the License. *    You may obtain a copy of the License at * *       http://www.apache.org/licenses/LICENSE-2.0 * *    Unless required by applicable law or agreed to in writing, software *    distributed under the License is distributed on an "AS IS" BASIS, *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *    See the License for the specific language governing permissions and *    limitations under the License. */package org.apache.ibatis.executor;import java.sql.SQLException;import java.util.List;import org.apache.ibatis.cache.Cache;import org.apache.ibatis.cache.CacheKey;import org.apache.ibatis.cache.TransactionalCacheManager;import org.apache.ibatis.cursor.Cursor;import org.apache.ibatis.mapping.BoundSql;import org.apache.ibatis.mapping.MappedStatement;import org.apache.ibatis.mapping.ParameterMapping;import org.apache.ibatis.mapping.ParameterMode;import org.apache.ibatis.mapping.StatementType;import org.apache.ibatis.reflection.MetaObject;import org.apache.ibatis.session.ResultHandler;import org.apache.ibatis.session.RowBounds;import org.apache.ibatis.transaction.Transaction;/** * @author Clinton Begin * @author Eduardo Macarron */public class CachingExecutor implements Executor {  // 有点类似于流的设计,这里的其实就是SimpleExecutor执行  private Executor delegate;  private TransactionalCacheManager tcm = new TransactionalCacheManager();  public CachingExecutor(Executor delegate) {    this.delegate = delegate;    delegate.setExecutorWrapper(this);  }  @Override  public Transaction getTransaction() {    return delegate.getTransaction();  }  @Override  public void close(boolean forceRollback) {    try {      //issues #499, #524 and #573      if (forceRollback) {         tcm.rollback();      } else {        tcm.commit();      }    } finally {      delegate.close(forceRollback);    }  }  @Override  public boolean isClosed() {    return delegate.isClosed();  }  @Override  public int update(MappedStatement ms, Object parameterObject) throws SQLException {    flushCacheIfRequired(ms);    return delegate.update(ms, parameterObject);  }  @Override  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {    BoundSql boundSql = ms.getBoundSql(parameterObject);    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);  }  @Override  public <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException {    flushCacheIfRequired(ms);    return delegate.queryCursor(ms, parameter, rowBounds);  }  // 执行查询操作;先处理cache,然后交给SimpleExecutor处理  @Override  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)      throws SQLException {    Cache cache = ms.getCache();    if (cache != null) {      flushCacheIfRequired(ms);      if (ms.isUseCache() && resultHandler == null) {        ensureNoOutParams(ms, parameterObject, boundSql);        @SuppressWarnings("unchecked")        List<E> list = (List<E>) tcm.getObject(cache, key);        if (list == null) {          list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);          tcm.putObject(cache, key, list); // issue #578 and #116        }        return list;      }    }    return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);  }  @Override  public List<BatchResult> flushStatements() throws SQLException {    return delegate.flushStatements();  }  @Override  public void commit(boolean required) throws SQLException {    delegate.commit(required);    tcm.commit();  }  @Override  public void rollback(boolean required) throws SQLException {    try {      delegate.rollback(required);    } finally {      if (required) {        tcm.rollback();      }    }  }  private void ensureNoOutParams(MappedStatement ms, Object parameter, BoundSql boundSql) {    if (ms.getStatementType() == StatementType.CALLABLE) {      for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) {        if (parameterMapping.getMode() != ParameterMode.IN) {          throw new ExecutorException("Caching stored procedures with OUT params is not supported.  Please configure useCache=false in " + ms.getId() + " statement.");        }      }    }  }  @Override  public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {    return delegate.createCacheKey(ms, parameterObject, rowBounds, boundSql);  }  @Override  public boolean isCached(MappedStatement ms, CacheKey key) {    return delegate.isCached(ms, key);  }  @Override  public void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType) {    delegate.deferLoad(ms, resultObject, property, key, targetType);  }  @Override  public void clearLocalCache() {    delegate.clearLocalCache();  }  private void flushCacheIfRequired(MappedStatement ms) {    Cache cache = ms.getCache();    if (cache != null && ms.isFlushCacheRequired()) {            tcm.clear(cache);    }  }  @Override  public void setExecutorWrapper(Executor executor) {    throw new UnsupportedOperationException("This method should not be called");  }}主要是做缓存相关处理,然后将信息交给SimpleExecutor执行.

SimpleExecutor.java

/** *    Copyright 2009-2016 the original author or authors. * *    Licensed under the Apache License, Version 2.0 (the "License"); *    you may not use this file except in compliance with the License. *    You may obtain a copy of the License at * *       http://www.apache.org/licenses/LICENSE-2.0 * *    Unless required by applicable law or agreed to in writing, software *    distributed under the License is distributed on an "AS IS" BASIS, *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *    See the License for the specific language governing permissions and *    limitations under the License. */package org.apache.ibatis.executor;import org.apache.ibatis.cursor.Cursor;import org.apache.ibatis.executor.statement.StatementHandler;import org.apache.ibatis.logging.Log;import org.apache.ibatis.mapping.BoundSql;import org.apache.ibatis.mapping.MappedStatement;import org.apache.ibatis.session.Configuration;import org.apache.ibatis.session.ResultHandler;import org.apache.ibatis.session.RowBounds;import org.apache.ibatis.transaction.Transaction;import java.sql.Connection;import java.sql.SQLException;import java.sql.Statement;import java.util.Collections;import java.util.List;/** * @author Clinton Begin */public class SimpleExecutor extends BaseExecutor {  public SimpleExecutor(Configuration configuration, Transaction transaction) {    super(configuration, transaction);  }  // 处理更新操作  @Override  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {    Statement stmt = null;    try {      Configuration configuration = ms.getConfiguration();      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);      stmt = prepareStatement(handler, ms.getStatementLog());      return handler.update(stmt);    } finally {      closeStatement(stmt);    }  }  // 处理查询操作  @Override  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {    Statement stmt = null;    try {      Configuration configuration = ms.getConfiguration();      // 获取SQL处理器对象,这里获取的是RoutingStatementHandler路由处理器      // 然后会进行路由转化到      /**       switch (ms.getStatementType()) {      case STATEMENT:        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);        break;      case PREPARED:        delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);        break;      case CALLABLE:        delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);        break;      default:        throw new ExecutorException("Unknown statement type: " + ms.getStatementType());    }    * 实际上会经过三个阶段:运输、准备、调用    */      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);      return handler.<E>query(stmt, resultHandler);    } finally {      closeStatement(stmt);    }  }  @Override  protected <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException {    Configuration configuration = ms.getConfiguration();    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, null, boundSql);    Statement stmt = prepareStatement(handler, ms.getStatementLog());    return handler.<E>queryCursor(stmt);  }  @Override  public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {    return Collections.emptyList();  }  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {    Statement stmt;    Connection connection = getConnection(statementLog);    stmt = handler.prepare(connection, transaction.getTimeout());    handler.parameterize(stmt);    return stmt;  }}

PreparedStatementHandler.java

/** *    Copyright 2009-2015 the original author or authors. * *    Licensed under the Apache License, Version 2.0 (the "License"); *    you may not use this file except in compliance with the License. *    You may obtain a copy of the License at * *       http://www.apache.org/licenses/LICENSE-2.0 * *    Unless required by applicable law or agreed to in writing, software *    distributed under the License is distributed on an "AS IS" BASIS, *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *    See the License for the specific language governing permissions and *    limitations under the License. */package org.apache.ibatis.executor.statement;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import java.util.List;import org.apache.ibatis.cursor.Cursor;import org.apache.ibatis.executor.Executor;import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;import org.apache.ibatis.executor.keygen.KeyGenerator;import org.apache.ibatis.mapping.BoundSql;import org.apache.ibatis.mapping.MappedStatement;import org.apache.ibatis.session.ResultHandler;import org.apache.ibatis.session.RowBounds;/** * @author Clinton Begin */public class PreparedStatementHandler extends BaseStatementHandler {  public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {    super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);  }  @Override  public int update(Statement statement) throws SQLException {    PreparedStatement ps = (PreparedStatement) statement;    ps.execute();    int rows = ps.getUpdateCount();    Object parameterObject = boundSql.getParameterObject();    KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();    keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);    return rows;  }  @Override  public void batch(Statement statement) throws SQLException {    PreparedStatement ps = (PreparedStatement) statement;    ps.addBatch();  }  @Override  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {    PreparedStatement ps = (PreparedStatement) statement;    ps.execute();    return resultSetHandler.<E> handleResultSets(ps);  }  @Override  public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {    PreparedStatement ps = (PreparedStatement) statement;    ps.execute();    return resultSetHandler.<E> handleCursorResultSets(ps);  }  @Override  protected Statement instantiateStatement(Connection connection) throws SQLException {    String sql = boundSql.getSql();    if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {      String[] keyColumnNames = mappedStatement.getKeyColumns();      if (keyColumnNames == null) {        return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);      } else {        return connection.prepareStatement(sql, keyColumnNames);      }    } else if (mappedStatement.getResultSetType() != null) {      return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);    } else {      return connection.prepareStatement(sql);    }  }  // 设置SQL参数  @Override  public void parameterize(Statement statement) throws SQLException {    parameterHandler.setParameters((PreparedStatement) statement);  }}

DefaultParameterHandler.java

/** *    Copyright 2009-2015 the original author or authors. * *    Licensed under the Apache License, Version 2.0 (the "License"); *    you may not use this file except in compliance with the License. *    You may obtain a copy of the License at * *       http://www.apache.org/licenses/LICENSE-2.0 * *    Unless required by applicable law or agreed to in writing, software *    distributed under the License is distributed on an "AS IS" BASIS, *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *    See the License for the specific language governing permissions and *    limitations under the License. */package org.apache.ibatis.scripting.defaults;import java.sql.PreparedStatement;import java.sql.SQLException;import java.util.List;import org.apache.ibatis.executor.ErrorContext;import org.apache.ibatis.executor.parameter.ParameterHandler;import org.apache.ibatis.mapping.BoundSql;import org.apache.ibatis.mapping.MappedStatement;import org.apache.ibatis.mapping.ParameterMapping;import org.apache.ibatis.mapping.ParameterMode;import org.apache.ibatis.reflection.MetaObject;import org.apache.ibatis.session.Configuration;import org.apache.ibatis.type.JdbcType;import org.apache.ibatis.type.TypeException;import org.apache.ibatis.type.TypeHandler;import org.apache.ibatis.type.TypeHandlerRegistry;/** * @author Clinton Begin * @author Eduardo Macarron */public class DefaultParameterHandler implements ParameterHandler {  private final TypeHandlerRegistry typeHandlerRegistry;  private final MappedStatement mappedStatement;  private final Object parameterObject;  private BoundSql boundSql;  private Configuration configuration;  public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {    this.mappedStatement = mappedStatement;    this.configuration = mappedStatement.getConfiguration();    this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();    this.parameterObject = parameterObject;    this.boundSql = boundSql;  }  @Override  public Object getParameterObject() {    return parameterObject;  }  // 这只参数,然后这里就会使用到TypeHandler进行处理,  @Override  public void setParameters(PreparedStatement ps) {    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();    if (parameterMappings != null) {      for (int i = 0; i < parameterMappings.size(); i++) {        ParameterMapping parameterMapping = parameterMappings.get(i);        if (parameterMapping.getMode() != ParameterMode.OUT) {          Object value;          String propertyName = parameterMapping.getProperty();          if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params            value = boundSql.getAdditionalParameter(propertyName);          } else if (parameterObject == null) {            value = null;          } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {            value = parameterObject;          } else {            MetaObject metaObject = configuration.newMetaObject(parameterObject);            value = metaObject.getValue(propertyName);          }          // 获取类型处理器          TypeHandler typeHandler = parameterMapping.getTypeHandler();          JdbcType jdbcType = parameterMapping.getJdbcType();          if (value == null && jdbcType == null) {            jdbcType = configuration.getJdbcTypeForNull();          }          try {            // 调用typehandler的参数处理            typeHandler.setParameter(ps, i + 1, value, jdbcType);          } catch (TypeException e) {            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);          } catch (SQLException e) {            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);          }        }      }    }  }}

BaseTypeHandler.java

/** *    Copyright 2009-2015 the original author or authors. * *    Licensed under the Apache License, Version 2.0 (the "License"); *    you may not use this file except in compliance with the License. *    You may obtain a copy of the License at * *       http://www.apache.org/licenses/LICENSE-2.0 * *    Unless required by applicable law or agreed to in writing, software *    distributed under the License is distributed on an "AS IS" BASIS, *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *    See the License for the specific language governing permissions and *    limitations under the License. */package org.apache.ibatis.type;import java.sql.CallableStatement;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import org.apache.ibatis.executor.result.ResultMapException;import org.apache.ibatis.session.Configuration;/** * @author Clinton Begin * @author Simone Tripodi */public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {  protected Configuration configuration;  public void setConfiguration(Configuration c) {    this.configuration = c;  }  // 核心方法,目的就是设置和映射参数  @Override  public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {    if (parameter == null) {      if (jdbcType == null) {        throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");      }      try {        ps.setNull(i, jdbcType.TYPE_CODE);      } catch (SQLException e) {        throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " +                "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " +                "Cause: " + e, e);      }    } else {      try {        // 主要方法        setNonNullParameter(ps, i, parameter, jdbcType);      } catch (Exception e) {        throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . " +                "Try setting a different JdbcType for this parameter or a different configuration property. " +                "Cause: " + e, e);      }    }  }  @Override  public T getResult(ResultSet rs, String columnName) throws SQLException {    T result;    try {      result = getNullableResult(rs, columnName);    } catch (Exception e) {      throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set.  Cause: " + e, e);    }    if (rs.wasNull()) {      return null;    } else {      return result;    }  }  @Override  public T getResult(ResultSet rs, int columnIndex) throws SQLException {    T result;    try {      result = getNullableResult(rs, columnIndex);    } catch (Exception e) {      throw new ResultMapException("Error attempting to get column #" + columnIndex+ " from result set.  Cause: " + e, e);    }    if (rs.wasNull()) {      return null;    } else {      return result;    }  }  @Override  public T getResult(CallableStatement cs, int columnIndex) throws SQLException {    T result;    try {      result = getNullableResult(cs, columnIndex);    } catch (Exception e) {      throw new ResultMapException("Error attempting to get column #" + columnIndex+ " from callable statement.  Cause: " + e, e);    }    if (cs.wasNull()) {      return null;    } else {      return result;    }  }  public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;  public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;  public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;  public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException;}

执行路径

SqlSessionFactory.build(加载配置文件) -> 会实例化Configuration对象 -> openSession创建 DefaultSqlSession -> select/update操作 ->主要通过执行器(CachingExecutor和SimpleExecutor执行) -> 经过一些处理器例如:RoutingStatementHandler进行转化到 PreparedStatementHandler 进行准备处理 ->然后进行注入格式化参数(DefaultParameterHandler)-> 调用setParameters方法来实现typeHandler的映射操作。

项目地址: https://github.com/wzpthq/csdn_mybatis.git

0 0
原创粉丝点击