【mybatis】 mybatis 工作原理底层源码全流程解析「建议收藏」

【mybatis】 mybatis 工作原理底层源码全流程解析「建议收藏」mybatis 工作原理 1. SqlSessionFactory 初始化 作用:加载Mybatis配置,用于生成SqlSession用于数据库操作 // 加载mybatis全局配置文件,生成Sql…

【mybatis】 mybatis 工作原理底层源码全流程解析

mybatis 工作原理

1. SqlSessionFactory 初始化

作用:加载Mybatis配置,用于生成SqlSession用于数据库操作

// 加载mybatis全局配置文件,生成SqlSessionFactory
InputStream inputStream = Resources.getResourceAsStream(factedfilePath);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

代码100分

全局配置文件:SqlMapConfig.xml

代码100分<configuration>

    <!-- myybatis 变量 -->
    <properties resource="db.properties"/>

    <!-- 实体别名,用于ResultMap映射 -->
    <typeAliases>
        <package name="com.zhiwei.entity"/>
    </typeAliases>
    
    <!-- 累心处理器:用于ResultSetHandler解析 -->
    <typeHandlers>
        <typeHandler handler="com.zhiwei.advanced.type.AddressTypeHandler"/>
    </typeHandlers>

    <!-- 环境配置: 主要包含数据库配置-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.user}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

    <!-- mapper接口映射:classpath下,注意maven打包需配置引入XML资源 -->
    <mappers>
        <mapper resource="advanced/StudentMapper.xml"/>
    </mappers>
</configuration>

1.0 解析SqlMapConfig.xml

代码:org.apache.ibatis.session.SqlSessionFactoryBuilder#build

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      // 本质由XMLConfigBuilder解析生成Configuration对象,由Configuration初始化SqlSessionFactory
       parser = new XMLConfigBuilder(inputStream, environment, properties);
      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.
      }
    }
  }

1.1 XMLConfigBuilder解析配置,生成Configuration

Configuration: 包含mybatis所有环境配置信息:例如

  • MapperRegistry: mapper接口注册信息,用于后续动态代理处理数据库逻辑
  • InterceptorChain: mybatis拦截器,用于mybatis各个阶段功能加强,例如监控、分页等
  • TypeHandlerRegistry: 用于ResultSetHandler解析ResultSet时的数据转换,例如数据库存储JOSN字符串,可解析成对象
  • TypeAliasRegistry:类型别名注册,包含指定类的别名,ResultMap时可直接映射
  • mappedStatements:包含Mapper.xml文件配置所有信息,对应CRUD的XML片段(业务执行核心)
  • keyGenerators: 主键自动生成器,类似mysql auto_increment 主键自动增长功能
  • resultMaps: resultMap 配置信息(数据库响应字段与实体映射)
  • parameterMaps: parameterMap 配置信息(请求实体字段与数据库字段映射)
  • environment: 环境信息,包含数据库相关配置,封装数据源和事务工厂

源码:org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration

代码100分private void parseConfiguration(XNode root) {
    try {
      //解析mybatis变量属性:对应标签:<properties resource="db.properties"/>
      propertiesElement(root.evalNode("properties"));

      //解析settings标签,并作做字段校验,防止非法字段
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      
      // 加载Vfs配置:虚拟文件系统配置数据:vfsImpl
      loadCustomVfs(settings);
        
      // 加载自定义日志实现 logImpl
      loadCustomLogImpl(settings);

      // 类别名解析:对应标签:案例将包com.zhiwei.entity下实力映射别名,例如类Student,别名为student,ResultMap可直接引用
      // <typeAliases>
      //        <package name="com.zhiwei.entity"/>
      //   </typeAliases>
      typeAliasesElement(root.evalNode("typeAliases"));

      //插件解析:用于Mybatis拦截器处理:interceptorChain
      pluginElement(root.evalNode("plugins"));

      // 对象工厂解析,用于创建对象、设置属性等操作(工具类)
      objectFactoryElement(root.evalNode("objectFactory"));

      // 对象包装器工厂,类似Spring Bean和BeanWrapper的关系,用于生成包装对象
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));

      // 反射器工厂:缓存类和类反射器,用于反射操作
      reflectorFactoryElement(root.evalNode("reflectorFactory"));

      //应用mybatis最终配置,例如cacheEnabled 默认开启二级缓存
      settingsElement(settings);

      //mybatis环境配置:主要封装数据库信息
      environmentsElement(root.evalNode("environments"));

      //databaseIdProvider:用于标识environment配置的数据源,例如Oracle、MYSQL产品名称
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));

      // 类型处理器:主要用于数据库查询返回后,字段与实体对应,例如数据库存储为JSON,实际实体属性为对象,这里就需类型处理器进行转换
      typeHandlerElement(root.evalNode("typeHandlers"));

      // mapper.xml解析: 底层实现:org.apache.ibatis.builder.xml.XMLMapperBuilder.parse
      // MappedStatement: 对应CRUD的SQL片段,唯一标识:namespace+id
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

1.2 MappedStatement 生成逻辑

案例:StudentMapper.xml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--
<mapper namespace="com.zhiwei.advanced.mapper.StudentMapper">
    <resultMap type="com.zhiwei.entity.Student" id="studentResultMap">
        <id column="sid" property="sid"/>
        <result column="name" property="name"/>
        <result column="password" property="password"/>
        <result column="age" property="age"/>
        <result column="gender" property="gender" javaType="java.lang.String"/>
        <result column="score" property="score" javaType="java.lang.Integer"/>
        <result column="address" property="address" typeHandler="com.zhiwei.advanced.type.AddressTypeHandler"/>
    </resultMap>

    <!-- MappedStatement主题 -->
    <select id="findStudentById" parameterType="int" resultMap="studentResultMap">
        select * from student where sid = #{sid}
    </select>
</mapper>

源码:org.apache.ibatis.builder.xml.XMLMapperBuilder.configurationElement

private void configurationElement(XNode context) {
    
    //context XNode对应标签:<mapper namespace="com.zhiwei.advanced.mapper.StudentMapper">

    try {
      //获取namespace: com.zhiwei.advanced.mapper.StudentMapper
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.equals("")) {
        throw new BuilderException("Mapper"s namespace cannot be empty");
      }
    
      //工具类MapperBuilderAssistant设置当前处理的Mapper文件,namespace标识
      builderAssistant.setCurrentNamespace(namespace);

      // 缓存引用处理,实现多个Mapper共享相同的缓存,缓存由Configuration caches维护
      cacheRefElement(context.evalNode("cache-ref"));

      // 缓存处理:mapper级别缓存控制,默认全局Setting(settingsElement(settings))配置cacheEnabled开启二级缓存,Configuration维护也一份缓存供其他Mapper引用:cache-ref
      cacheElement(context.evalNode("cache"));

      // parameterMap维护:用于映射请求实体和数据库字段(基本弃用,parameterType代替)
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));

      //resultMap 数据库查询结果集映射
      resultMapElements(context.evalNodes("/mapper/resultMap"));

      // sql解析,封装到MappedStatement sqlSource属性
      sqlElement(context.evalNodes("/mapper/sql"));

      // 构建MappedStatement对象(CRUD操作才被被映射成MappedStatement)
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. The XML location is "" + resource + "". Cause: " + e, e);
    }
  }

2. SqlSession 执行数据库操作

案例:

StudentMapper.java:
public interface StudentMapper {
    public Student findStudentById(Integer sid);
}

SqlSession sqlSession = sqlSessionFactory.openSession();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
studentMapper.findStudentById(id);

2.1 获取SqlSession的方式

案例:SqlSession sqlSession = sqlSessionFactory.openSession();
解析:因是Select操作,不设计数据变更,采用默认的autoCommit=true,自动提交事务,本质和java.sql.Connection.setAutoCommit(true)一样,执行1个操作就自动提交事务

底层源码:org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.openSessionFromDataSource

 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      // 获取mybatis环境变量,主要用于构建事务工厂,为后续数据库操作做好准备
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);

      // 基于数据源创建事务:JdbcTransaction(Connection 数据库连接、dataSource 数据源、level 数据库隔离级别、autoCommit 是否自动提交事务)
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
     
      // 执行器: 所有数据库操作底层都是交给Executor负责调度
      final Executor executor = configuration.newExecutor(tx, execType);

      // 创建SqlSession,包含mybatis配置、数据库操作执行器、事务自动提交标识
      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();
    }
  }

2.2 executor创建逻辑

源码:org.apache.ibatis.session.Configuration.newExecutor

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    //executorType: 执行器类型,默认Simple,可自定义:org.apache.ibatis.session.Configuration.defaultExecutorType
    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);
    }
    // 默认开启二级缓存,自动创建CachingExecutor,包装SimpleExecutor,二级缓存存储在TransactionalCacheManager,
    // 注意开启Mapper二级缓存需要定义cache标签或者引用别的Mapper缓存:cache-ref
    // 二级缓存开启步骤:
    // 1. 全局开启: 默认开启 (configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));)
    // 2. mapper.xml cache 开启
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

3. Mapper 生成逻辑(JDK 动态代理)

案例代码:StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);

MapperRegistry:映射器注册表,主要维护Mapper接口和代理对象工厂,用于生成Mapper代理对象,用以实际的数据库业务处理

底层源码:org.apache.ibatis.binding.MapperRegistry.getMapper

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      // 映射器动态代理工厂生成mapper接口动态代理对象
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

3.1 mapperProxyFactory生成Mapper动态代理对象

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

@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
    // JDK1.8 动态代理对象生成
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

3.2 MapperProxy 动态代理对象

MapperProxy实现动态代理接口:InvocationHandler

源码:org.apache.ibatis.binding.MapperProxy.invoke

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
  if (Object.class.equals(method.getDeclaringClass())) {
    return method.invoke(this, args);
  } else if (isDefaultMethod(method)) {
    return invokeDefaultMethod(proxy, method, args);
  }
} catch (Throwable t) {
  throw ExceptionUtil.unwrapThrowable(t);
}

//缓存method封装类:MapperMethod
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}

//缓存MapperMethod:包含Mapper接口、methon、mybatis环境配置
private MapperMethod cachedMapperMethod(Method method) {
    return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
  }

4. Mapper执行业务逻辑

案例代码:studentMapper.findStudentById(id);

分析:底层逻辑交由JDK动态代理对象执行,本质执行位置:org.apache.ibatis.binding.MapperProxy.invoke
代码:mapperMethod.execute(sqlSession, args);

org.apache.ibatis.binding.MapperMethod.execute

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      // sql insert 操作,对应 mapper.xml insert标签
      case INSERT: {
    	Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      // sql update操作:对应mapper.xml update标签
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      // sql delete 操作,对应mapper.xml delete标签
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      // sql select 操作,对应mapper.xml select标签
      case SELECT:
        // 根据返回值进行判断,进行对象的操作
        // 没有返回值,直接返回NULL
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        // 返回值为Collection或Array
        } else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
        //返回值为Map
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
        // 返回值为游标
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);

        // 其他默认处理: 案例属于这一种
        } else {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
          if (method.returnsOptional() &&
              (result == null || !method.getReturnType().equals(result.getClass()))) {
            result = Optional.ofNullable(result);
          }
        }
        break;
      
      // flush 操作: Mapper方法注解:@Flush,刷新Statement,释放数据库资源
      case FLUSH:
        result = sqlSession.flushStatements();
        break;
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method "" + command.getName()
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
  }

4.1 sqlSession执行数据库逻辑

案例代码: result = sqlSession.selectOne(command.getName(), param);

源码:org.apache.ibatis.session.defaults.DefaultSqlSession.selectList

  • RowBounds: mybatis 查询默认分页逻辑:0 ~ Integer.MAX_VALUE(2147483648, int存在符号位,只有31位标识数值:2^31-1)
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
  //从configuration缓存获取MappedStatement: statement(sql唯一标识): com.zhiwei.advanced.mapper.StudentMapper.findStudentById
  MappedStatement ms = configuration.getMappedStatement(statement);

  // 底层实际逻辑交给executor执行器处理,这里executor是CachingExecutor,因为默认开启二级缓存,sqlSession创建时自动创建,里面包装SimpleExecutor对象(装饰器模式),补充缓存功能
  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();
}
}

//请求参数封装类:如果请求参数是collection或array,则将参数存放到Map返回,若为原生对象则直接返回
 private Object wrapCollection(final Object object) {
    if (object instanceof Collection) {
      StrictMap<Object> map = new StrictMap<>();
      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<>();
      map.put("array", object);
      return map;
    }
    return object;
  }

4.2 CachingExecutor query代码(mybatis二级缓存)

源码:org.apache.ibatis.executor.CachingExecutor.query

@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
  throws SQLException {
//MappedStatement获取二级缓存:通过Configuration.cacheEnabled + cache/cache-ref 标签开启,注意缓存真实数据保存在MappedStatement自身,
// MappedStatement维护在Configuration内部从而实现相同Mapper不同session的缓存共享(二级缓存)
// cache-ref: 引用其他MappedStatement缓存,可理解为跨Mapper级别的二级缓存
Cache cache = ms.getCache();
if (cache != null) {
 //MappedStatement指定flushCache=true,则强制清理缓存,相当于每次访问都清空缓存
  flushCacheIfRequired(ms);
  
if (ms.isUseCache() && resultHandler == null) {
    ensureNoOutParams(ms, boundSql);
    @SuppressWarnings("unchecked")

    // sqlSession内部事务缓存获取,若存则直接返回,否则交给委托Executor执行:SimpleExecutor,并将结果存入二级缓存(相同Session)
    List<E> list = (List<E>) tcm.getObject(cache, key);
    if (list == null) {
      // 缓存不存在直接委托给SimpleExecutor执行
      list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
      tcm.putObject(cache, key, list); // issue #578 and #116
    }
    return list;
  }
}
// 全局开启二级缓存,但是Mapper.xml没有开启二级缓存则默认二级缓存无效,直接委托给SimpleExecutor执行
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

4.3 SimpleExecutor执行逻辑(mybatis一级缓存:localCache)

源码:org.apache.ibatis.executor.BaseExecutor.query

@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
  throw new ExecutorException("Executor was closed.");
}
//第一次查询或所有查询操作都完成并且mappedStatement flushCache配置true,强制清空本地缓存(一级缓存Session级别)
if (queryStack == 0 && ms.isFlushCacheRequired()) {
  clearLocalCache();
}
List<E> list;
try {
  queryStack++;
  // mybatis一级缓存获取结果集,存在则直接缓存,否则执行对应的查询逻辑
  list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
  if (list != null) {
    handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
  } else {
    // 具体数据库业务逻辑操作
    list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
  }
} finally {
  queryStack--;
}

// 第一次查询或所有查询操作都完成,进一步处理
if (queryStack == 0) {
  for (DeferredLoad deferredLoad : deferredLoads) {
    deferredLoad.load();
  }
  // issue #601
  deferredLoads.clear();
  //一级缓存细分的缓存作用域:默认 Configuration.localCacheScope(Session),可自定义为STATEMENT缓存
  if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
    // issue #482
    clearLocalCache();
  }
}
return list;
}

4.4 数据库业务底层处理

接口:org.apache.ibatis.executor.BaseExecutor.queryFromDatabase

private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    // 一级缓存数据更新,存放正在处理的标志EXECUTION_PLACEHOLDER, 标识缓存正在处理,缓存对外不能提供服务,不能加载
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
      // 数据库底层操作
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      // 清理本地缓存
      localCache.removeObject(key);
    }
    //更新本地缓存
    localCache.putObject(key, list);

    // 存储过程参数缓存
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }

4.5 数据库数据查询

接口:org.apache.ibatis.executor.SimpleExecutor.doQuery

@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();
      // 构造 StatementHandler, 主要用于生成JDBC的PrepreStatement(预编译解决SQL注入问题)
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

4.5.1 StatementHandler 构造逻辑

接口:org.apache.ibatis.session.Configuration.newStatementHandler

public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

//构造RoutingStatementHandler:本质是StatementHandler的装饰器,根据statementType构造不同的StatementHandler,案例 statementType为PREPARED, 对应PreparedStatementHandler
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);

//mybatis 拦截器处理(责任链模式):StatementHandler
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}

4.5.2 StatementHandler构造Statement(JDBC规范)

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    //transaction获取数据库连接:transaction(JdbcTransaction)
    Connection connection = getConnection(statementLog);

    // 获取 Statement
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
  }

org.apache.ibatis.executor.statement.BaseStatementHandler.prepare:
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
  // 初始化Statement
  statement = instantiateStatement(connection);

  // 设置查询和事务超时事件
  setStatementTimeout(statement, transactionTimeout);

  // 设置拉取数据量
  setFetchSize(statement);
  return statement;
} catch (SQLException e) {
  closeStatement(statement);
  throw e;
} catch (Exception e) {
  closeStatement(statement);
  throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
}
}

org.apache.ibatis.executor.statement.PreparedStatementHandler.instantiateStatement
// Statement初始化,本质遵循JDBC操作规范
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
  
//如果设置主键自动生成策略:Jdbc3KeyGenerator
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() == ResultSetType.DEFAULT) {
  return connection.prepareStatement(sql);

//其他类型
} else {
  return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
}
}

4.5.3 Statement参数化处理

案例:handler.parameterize(stmt);

底层源码:org.apache.ibatis.scripting.defaults.DefaultParameterHandler.setParameters

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);

        //请求参数映射模式,默认为ParameterMode.IN
        if (parameterMapping.getMode() != ParameterMode.OUT) {
          Object value; // 具体的请求参数的值
          String propertyName = parameterMapping.getProperty();
           //boundSql若boundSql有相同附加参数,直接赋值
          if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
            value = boundSql.getAdditionalParameter(propertyName);

           //请求参数对象为空则直接返回null
          } else if (parameterObject == null) {
            value = null;

          // 类型处理器是否包含指定请求参数类型,如果包含直接返回,原生支持(这里SID为Integer类型,直接支持)
          } 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 {
            // JDBC规范:prepareStatement参数赋值,类似如 select *from student where sid = ?, "?"为复制的地方,赋值完成后等价于sql=select *from student where sid = "xxx"
            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);
          }
        }
      }
    }
  }

4.5.4 执行SQL查询

案例代码:handler.query(stmt, resultHandler);

源码:org.apache.ibatis.executor.statement.PreparedStatementHandler.query

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
//执行prepareStatment
ps.execute();

//resultSetHandler处理查询操作后的结果集
return resultSetHandler.handleResultSets(ps);
}

4.5.5 SQL查询结果集处理resultSetHandler

接口:org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleResultSets

@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

final List<Object> multipleResults = new ArrayList<>();

int resultSetCount = 0;
//构建ResultSetWrapper(构造函数), 本质获取数据库表字段元数据,方便后续结果集封装
ResultSetWrapper rsw = getFirstResultSet(stmt);

// 获取ResultMap
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
  ResultMap resultMap = resultMaps.get(resultSetCount);
  // 数据库记录解析:ResultSetHandler解析解析为实体对象,对象保存在multipleResults集合
  handleResultSet(rsw, resultMap, multipleResults, null);
  rsw = getNextResultSet(stmt);
  cleanUpAfterHandlingResultSet();
  resultSetCount++;
}

// resultSets 默认为空,不进入处理
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
  while (rsw != null && resultSetCount < resultSets.length) {
    ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
    if (parentMapping != null) {
      String nestedResultMapId = parentMapping.getNestedResultMapId();
      ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
      handleResultSet(rsw, resultMap, null, parentMapping);
    }
    rsw = getNextResultSet(stmt);
    cleanUpAfterHandlingResultSet();
    resultSetCount++;
  }
}

//返回结果
return collapseSingleResultList(multipleResults);
}

…………………….. mybatis 整个工作流程梳理完成 …………………………

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/7547.html

(0)
上一篇 2023-03-18 15:30
下一篇 2023-03-18

相关推荐

  • 进入hive命令_hadoop怎么进入hive

    进入hive命令_hadoop怎么进入hive[root@node01 apache-hive-2.1.1-bin]# bin/hiveSLF4J: Class path contains multiple SLF4J bindings.SLF4

    2023-03-05
    170
  • 无效的Python SDK

    无效的Python SDKPython是一门高级编程语言,其简单易用、灵活多变的特点受到了众多开发者的喜爱。在Python生态圈中,开发者可以便捷地使用各种SDK开发自己的应用。但是,尽管Python的生态圈已经相当成熟和完善,仍然有一些Python SDK存在问题。本文主要讨论无效的Python SDK,这类SDK通常存在着质量不佳、未经充分测试、文档不完整等问题,使它们在生产环境中难以使用。

    2024-07-31
    39
  • 关于python超时控制源代码解读的信息

    关于python超时控制源代码解读的信息源代码是指原始代码,可以是任何语言代码。Python源码就是指编写的最原始程序的代码。运行的软件是要经过编写的,程序员编写程序的过程中需要他们的“语言”。

    2023-12-02
    140
  • Postgresql更新geometry类型字段「终于解决」

    Postgresql更新geometry类型字段「终于解决」执行如下语句即可: UPDATE hngsmmzt.hngs_gsmm_pt set geom=public.ST_SetSRID(public.ST_MakePoint(jd,wd),4490) …

    2023-01-30
    205
  • Python列表排序

    Python列表排序在编程中,需要处理的数据可能会以不同的方式进行分类和排序。Python中的列表是一种最常用的数据结构,因此排序是列表操作中最重要的部分之一。排序不仅可以让我们更好地理解数据,还可以让我们更有效地处理和展示不同类型的数据。在本文中,我们将以Python中的列表为例,深入探讨排序的理论和实践。

    2024-07-11
    43
  • SQLServer相关SQL语法及sqlcmd用法「建议收藏」

    SQLServer相关SQL语法及sqlcmd用法「建议收藏」sqlcmd用法 连接SQLServer数据库 sqlcmd -S localhost -U SA -P '' 导入SQL文件 qlcmd -S lo…

    2022-12-21
    149
  • 使用命令行从Python运行.py文件

    使用命令行从Python运行.py文件在实际开发过程中,我们可能会需要通过命令行运行Python文件,这种方式有其独特的优点。例如,命令行运行可以方便我们对脚本进行参数传递,调试过程更加直观,还可以在Linux服务器上运行Python代码。

    2024-02-22
    151
  • python查看所有库命令_python 列表第一个元素

    python查看所有库命令_python 列表第一个元素问题出在你每次得到的都是同一个list 每次取list[0] 当然永远是重复的第一个元素。

    2023-08-24
    133

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注