mybatis源码阅读

配置解析

首先来看一个简单使用例子

String resource = "mybatis-config.xml";
//读取配置,创建sessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream(resource));
//opensession
SqlSession sqlSession = sessionFactory.openSession();
//获取mapper
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//执行接口方法
User user = mapper.findUserByNo("001");

下面会针对上面的步骤进行分析执行过程。读取配置会将配置文件中的配置读取到Configuration对象中进行存储。几个重要的属性

//存储已经解析的mapper文件类型,内部有map存储
MapperRegistry mapperRegistry;
//类型处理器注册表
TypeHandlerRegistry typeHandlerRegistry;
//类型昵称注册表
TypeAliasRegistry typeAliasRegistry;
//存储配置的增删改查操作语句
Map<String, MappedStatement> mappedStatements;
//resultMap映射集
Map<String, ResultMap> resultMaps;
//缓存集
Map<String, Cache> caches;
...
//还有很多公共的setting配置信息

XMLConfigBuilder.parse()会解析配置文件,将配置信息存储到Configuration对象中。

sessionFactory在opensession的时候会将解析的configuration信息传递给session。然后session.getMapper会从mapperRegistry中检查mapper是否已注册,如果已注册返回mapper接口类型的代理类,否则抛出异常。

动态代理

所有的mapper都是接口,这个时候调用mapper接口方法时候就是用的动态代理构建实例对象。

通过session获取mapper实例就是构造代理的过程。session的getMapper方法会调用Configuration的getMapper然后是MapperRegistry的。

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 {//通过MapperProxyFactory获取代理实例对象
    return mapperProxyFactory.newInstance(sqlSession);
  } catch (Exception e) {
    throw new BindingException("Error getting mapper instance. Cause: " + e, e);
  }
}

MapperProxyFactory获取代理对象方法

public T newInstance(SqlSession sqlSession) {
  final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
  return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
  //通过JDK的Proxy类构造代理对象
  return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

MapperProxy类实现了InvocationHandler接口,其invoker方法执行会构造一个PlainMethodInvoker对象,最后会调用MapperMethod.execute()方法,来完成我们mapper接口的具体实现。来看看这个execute方法。

public Object execute(SqlSession sqlSession, Object[] args) {
  Object result;
  switch (command.getType()) {
    case INSERT: {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.insert(command.getName(), param));
      break;
    }
    case UPDATE: {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.update(command.getName(), param));
      break;
    }
    case DELETE: {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.delete(command.getName(), param));
      break;
    }
    case SELECT:
      if (method.returnsVoid() && method.hasResultHandler()) {
        executeWithResultHandler(sqlSession, args);
        result = null;
      } else if (method.returnsMany()) {
        result = executeForMany(sqlSession, args);
      } 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);
        ...
      }
      break;
    case FLUSH:
      result = sqlSession.flushStatements();
      break;
    default:
      ...
  }
  return result;
}

这个execute方法会根据增删改查不同的指令进行相应的操作。

执行SQL

sql指令的发出都是通过DefaultSqlSession方法。

查询类:selectOne和selectList。selectOne会通过selectList来完成

修改:insert和update最后都会调用update

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.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;
  }
}

DefaultSqlSession.selectList()方法会调用Executor的query方法。session中的executor实例是CachingExecutor。

CachingExecutor.query(),

CachingExecutor会有一个SimpleExecutor类型的Executor来真正执行查询,SimpleExecutor继承自BaseExecutor,BaseExecutor实现了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;
  localCache.putObject(key, EXECUTION_PLACEHOLDER);
  try {//doQuery调用的SimpleExecutor类方法
    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;
}

SimpleExecutor.doQuery

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();
    //这里创建的handler是RoutingStatementHandler
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
    stmt = prepareStatement(handler, ms.getStatementLog());
    return handler.query(stmt, resultHandler);
  } finally {
    closeStatement(stmt);
  }
}

RoutingStatementHandler.query

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  //这里的delegate是PreparedStatementHandler
  return delegate.query(statement, resultHandler);
}

PreparedStatementHandler.query

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  ps.execute();//执行查询
  //返回值处理
  return resultSetHandler.handleResultSets(ps);
}

最后还是通过PreparedStatement执行的sql操作。

返回值处理

  • 调用流程

以查询为例,DefaultResultSetHandler用来处理返回值。前面sql执行完后会调用DefaultResultSetHandler的handleResultSets(Statement stmt),实际处理方法在handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List multipleResults, ResultMapping parentMapping),然后调用handleRowValues,最后调用handleRowValuesForSimpleResultMap方法一行行获取数据。

  • 返回类型实例化

DefaultResultSetHandler.createResultObject()

private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
    throws SQLException {
  final Class<?> resultType = resultMap.getType();
  final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
  final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
  //是否时基本类型或者有配置类型handler
  if (hasTypeHandlerForResultObject(rsw, resultType)) {
    return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
  } else if (!constructorMappings.isEmpty()) {
    return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
  } else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
  //反射创建
    return objectFactory.create(resultType);
  } else if (shouldApplyAutomaticMappings(resultMap, false)) {
  //构造方法映射直接创建
    return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs);
  }
  throw new ExecutorException("Do not know how to create an instance of " + resultType);
}

DefaultObjectFactory,通过反射创建返回类型实例

private  <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
	//无参
    Constructor<T> constructor constructor = type.getDeclaredConstructor();
    return constructor.newInstance();;
    if (constructorArgTypes == null || constructorArgs == null) {
      
    //有参  
    constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[0]));
	return constructor.newInstance(constructorArgs.toArray(new Object[0]))
}
  • 数据属性值注入

数据赋值,根据查询的列进行bean数据的赋值。columns来源于ResultSet。这里是以数据库ResultSet查询

ResultSetWrapper初始化的时候会获取到列名

public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException {
  super();
  this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
  this.resultSet = rs;
  final ResultSetMetaData metaData = rs.getMetaData();
  final int columnCount = metaData.getColumnCount();
  //获取所有的列
  for (int i = 1; i <= columnCount; i++) {
    columnNames.add(configuration.isUseColumnLabel() ? metaData.getColumnLabel(i) : metaData.getColumnName(i));
    jdbcTypes.add(JdbcType.forCode(metaData.getColumnType(i)));
    classNames.add(metaData.getColumnClassName(i));
  }
}

来看具体一行数据赋值处理方法getRowValue

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
  final ResultLoaderMap lazyLoader = new ResultLoaderMap();
  Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
  if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
    final MetaObject metaObject = configuration.newMetaObject(rowValue);
    boolean foundValues = this.useConstructorMappings;
    //是否走自动映射,这里会根据配置的autoMappingBehavior属性进行判断
    if (shouldApplyAutomaticMappings(resultMap, false)) {
      //自动映射
      foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
    }
    //最后显示映射兜底,这里就是根据resultmap配置的property和colum映射关系进行赋值
    foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
    foundValues = lazyLoader.size() > 0 || foundValues;
    //最后根据配置的returnInstanceForEmptyRow属性判断,如果查询为空是返回空对象还是null
    rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
  }
  return rowValue;
}

自动映射逻辑,这里就是根据列名找属性名的过程

 private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
    final String mapKey = resultMap.getId() + ":" + columnPrefix;
    List<UnMappedColumnAutoMapping> autoMapping = autoMappingsCache.get(mapKey);
    if (autoMapping == null) {
      autoMapping = new ArrayList<>();
      //获取所有查询的列名
      final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
      for (String columnName : unmappedColumnNames) {
        String propertyName = columnName;
        //前缀处理
        if (columnPrefix != null && !columnPrefix.isEmpty()) {
          // When columnPrefix is specified,
          // ignore columns without the prefix.
          if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
            propertyName = columnName.substring(columnPrefix.length());
          } else {
            continue;
          }
        }
        //返回类型对象是否有该属性
        final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
        if (property != null && metaObject.hasSetter(property)) {//有该属性并且有set方法
          if (resultMap.getMappedProperties().contains(property)) {//跳过已经显式映射过的属性
            continue;
          }
          //获取set方法入参类型,判断是否有类型处理器
          final Class<?> propertyType = metaObject.getSetterType(property);
          if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {
            final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
            //找到TypeHandler,添加到自动类型映射map里
            autoMapping.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));
          } else {//自动映射未知列处理
            configuration.getAutoMappingUnknownColumnBehavior()
                .doAction(mappedStatement, columnName, property, propertyType);
          }
        } else {//未知字段处理
          configuration.getAutoMappingUnknownColumnBehavior()
              .doAction(mappedStatement, columnName, (property != null) ? property : propertyName, null);
        }
      }
      //加入缓存中
      autoMappingsCache.put(mapKey, autoMapping);
    }
    return autoMapping;
  }

最后返回的autoMapping是{列名,属性名,typeHandler},回到applyAutomaticMappings方法里对属性进行赋值

private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
  List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
  boolean foundValues = false;
  if (!autoMapping.isEmpty()) {
    for (UnMappedColumnAutoMapping mapping : autoMapping) {
    //使用typeHandler从rs里获取对应属性的value值
      final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
      if (value != null) {
        foundValues = true;
      }
      if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
        // gcode issue #377, call setter on nulls (value is not 'found')
        metaObject.setValue(mapping.property, value);
      }
    }
  }
  return foundValues;
}

自动注入值完成后,会根据resultMap进行映射调用applyPropertyMappings方法赋值。这里就不用多看了,因为resultMap里已经明确了column和property的映射关系。最后找不到对应的column会根据配置的autoMappingBehavior属性来确定如何处理。

总结

mybatis初始化会load所有的配置信息到Configuration对象中。在获取mapper接口的时候是返回其动态代理对象,代理的handler是MapperProxy类,其invoker方法执行会构造一个PlainMethodInvoker对象,最后会调用MapperMethod.execute()方法,execute方法会根据不同的command(增删改查)来构造不同的PrepareStatement,最后都是通过PrepareStatement来执行具体的sql。如果是查询返回值会根据resultMap映射进行colum到property的赋值,如果开启了自动映射,对未明确的列会先进行自动注入属性值。前提是colum和property名称相等。

posted @ 2023-08-10 19:18  朋羽  阅读(7)  评论(0编辑  收藏  举报