Mybatis-运行原理

≯℡__Kan透↙ 提交于 2019-12-16 19:03:41
  1. 构建 SqlFactory 过程:
    • SqlFactory 的核心功能就是提供创建 MyBatis 的核心接口 SqlSession. SqlSession 采用建造者模式进行构建:
      • 首先通过 org.apache.ibatis.builder.xml.XMLConfigBuilder 解析配置的 XML 文件, 读出所配置的参数, 并将读取的内容存入 org.apache.ibatis.session.Configuration 类对象中. Configuration 对象采用的是单例模式.
      • 通过 Configuration 对象去创建 SqlSessionFactory. SqlSessionFactory 是一个接口, MyBatis 提供了一个默认的实现类 org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.
        private void parseConfiguration(XNode root) {
            try {
                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);
                environmentsElement(root.evalNode("environments"));
                databaseIdProviderElement(root.evalNode("databaseIdProvider"));
                // Java 数据类型和数据库数据类型转换器
                typeHandlerElement(root.evalNode("typeHandlers"));
                mapperElement(root.evalNode("mappers"));
            } catch (Exception e) {
                throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
            }
        }
        
        配置 TypeHandler 都会被注册到 typeHandlerRegistry 对象中去, 而 typeHandlerRegistry 是定义在 XMLConfigBuilder 的父类 BaseBuilder 中的
        private void typeHandlerElement(XNode parent) throws Exception {
            if (parent != null) {
                for (XNode child : parent.getChildren()) {
                    if ("package".equals(child.getName())) {
                        String typeHandlerPackage = child.getStringAttribute("name");
                        typeHandlerRegistry.register(typeHandlerPackage);
                    } else {
                        String javaTypeName = child.getStringAttribute("javaType");
                        String jdbcTypeName = child.getStringAttribute("jdbcType");
                        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);
                        }
                    }
                }
            }
        }
        
        public abstract class BaseBuilder {
            protected final Configuration configuration;
            protected final TypeAliasRegistry typeAliasRegistry;
            protected final TypeHandlerRegistry typeHandlerRegistry;
        }
        
        
  2. 构建映射器的内部组成:
    MyBatis 中一条 SQL 和它相关的配置信息由: MappedStatement, SqlSource, BoundSql 3 个部分组成:
    • MappedStatement: 保存一个映射节点的内容, 也就是 mapper.xml 文件中的配置的相关信息. 它有一个重要的属性 sqlSource.
    • SqlSource: 提供 BoundSql.
    • BoundSql: 是 SqlSource 通过对 SQL 和参数的联合解析得到的 SQL 和参数. 它有 3 个常用的属性: sql, parameterObject, paramterMappings.
      • parameterObject: 是描述 Mapper 接口中方法传递的参数. 当传递基本类型时, MyBatis 会将其转化为对应的包装类型; 当传递多个参数且没有使用 @Param 注解时, MyBatis 会把 parameterObject 变为一个 Map<String, Object> 对象, 如果使用 @Param 注解, 则会将 Map 的 key 设置 @Param 注解设置的名称;
        /****未使用 @Param 注解*****/
        // Mapper 接口方法定义
        User selectByNameAndAddress(String name, String address);
        
        // 最终生成的 parameterObject
        Map<String, String> parameterObject = new HashMap<>();
        parameterObject.put("arg0", "xiaoli");
        parameterObject.put("param1", "xiaoli");
        parameterObject.put("arg1", "beijing");
        parameterObject.put("param2", "beijing");
        
        /****使用 @Param 注解*****/
        User selectByNameAndAddress(@Param("name")String name, @Param("address")String address);
        
        // 最终生成的 parameterObject
        Map<String, String> parameterObject = new HashMap<>();
        parameterObject.put("name", "xiaoli");
        parameterObject.put("param1", "xiaoli");
        parameterObject.put("address", "beijing");
        parameterObject.put("param2", "beijing");
        
      • parameterMappings: 是一个 List, 它的每一个元素都是 ParameterMapping 对象. ParameterMapping 对象是描述 mapper.xml 中 SQL 语句定义的参数.
            <select id="selectByNameAndAddress" resultType="study.User">
                SELECT id,
                       name,
                       address
                  FROM user
                 WHERE id = #{id}
                   AND name = #{name}      
            </select>
        
        最终生成的 parameterMappings 就包括两个对象, 一个是 id 对应的 ParamterMapping 对象, 一个是 name 对应的 ParameterMapping 对象.
      • sql: 是 mapper.xml 里面的一条被 SqlSource 解析后的 SQL.
  3. SqlSession 运行过程:
    首先生成 Mapper 接口的动态代理对象 MapperProxy 的实例, 当调用 Mapper 接口定义的方法时, 在 MapperProxy 实例里面将方法映射到 SqlSession#execute() 方法上执行, 最终再调用底层 Executor 的方法, 最终才调用 JDBC 的方法.
    // 调用 SqlSession#getMapper() 方法生成动态代理对象
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    
    // SqlSession 实现
    public class DefaultSqlSession implements SqlSession {
        @Override
        public <T> getMapper(Class<T> type) {
            // 这里将 SqlSession 当作参数传递进去
            return configuration.<T>getMapper(type, this);
        }
    }
    
    // Configuration 实现
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        // mapperRegistry 是在构建 Configuration 时, 通过读取配置文件完成注册的
        return mapperRegistry.getMapper(type, sqlSession);
    }
    
    // MapperRegistry 实现
    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);
        }
    }
    
    // MapperProxyFactory 实现
    protected T newInstance(MapperProxy<T> mapperProxy) {
        // 这里生成 Mapper 接口的代理对象
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), 
        new Class[] { mapperInterface }, mapperProxy);
    }
    
    public T newInstance(SqlSession sqlSession) {
        final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
        return newInstance(mapperProxy);
    }
    
    // MapperProxy 实现
    public class MapperProxy<T> implements InvocationHandler, Serializable {
        // 调用任何一个 Mapper 接口的方法都会进入到这里
        @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);
            }
            final MapperMethod mapperMethod = cachedMapperMethod(method);
            // 这里调用 SqlSession 的
            return mapperMethod.execute(sqlSession, args);
        }
    }
    
    // MapperMethod 实现
    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()) {
                //  这里调用了 executeForMany() 方法
                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:
            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;
    }
    
    private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
        List<E> result;
        Object param = method.convertArgsToSqlCommandParam(args);
        if (method.hasRowBounds()) {
            RowBounds rowBounds = method.extractRowBounds(args);
            // 这里又调用了 sqlSession#selectList() 方法
            result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
        } else {
            result = sqlSession.<E>selectList(command.getName(), param);
        }
        if (!method.getReturnType().isAssignableFrom(result.getClass())) {
            if (method.getReturnType().isArray()) {
                return convertToArray(result);
            } else {
                return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
            }
        }
        return result;
    }
    
    @Override
    public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        try {
            MappedStatement ms = configuration.getMappedStatement(statement);
            // 这里又调用 Executor#query() 方法
            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();
        }
    }
    
  4. SqlSession 下的四大对象:
    • Executor: SqlSession 利用门面模式将 Executor 方法封装不同方法提供给客户端使用. Executor 是真正执行 Java 和数据库交互的对象. MyBatis 三种执行器:
      • SIMPLE: 简易执行器, 默认的执行器
      • REUSE: 能够执行重用预处理语句的执行器
      • BATCH: 执行器重用语句和批量更新
        执行器是在 Configuration 类当中生成的
      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;
      }
      
    • StatementHandler: 处理数据库会话. 数据库会话器也是在 Configuration 类当中生成的
      public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement,        Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
          // 这里生成的是 RoutingStatementHandler 
          StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
          statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
          return statementHandler;
      }
      
      public class RoutingStatementHandler implements StatementHandler {
      
          private final StatementHandler delegate;
      
          public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter,            RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
      
              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());
              }
      
          }
      }
      
      // Executor 中调用 StatementHandler 
      private Statement prepareStatement(StatementHandler handler, 
          Log statementLog) throws SQLException {
          Statement stmt;
          Connection connection = getConnection(statementLog);
          // 调用 StatementHandler#prepare() 方法, 做一些基础配置
          stmt = handler.prepare(connection, transaction.getTimeout());
          // 调用 StatementHandler#parameterize() 方法设置参数
          handler.parameterize(stmt);
          return stmt;
      }
      // 在 StatementHandler 调用 ResultHandler 处理结果
      @Override
      public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
          String sql = boundSql.getSql();
          statement.execute(sql);
          // 调用 ResultHandler#handleResultSets() 方法
          return resultSetHandler.<E>handleResultSets(statement);
      }
      
    • ParameterHandler: 参数处理器, 对预编译语句进行参数设置
      @Override
      public void setParameters(PreparedStatement ps) {
          ErrorContext.instance().activity("setting parameters")
          .object(mappedStatement.getParameterMap().getId());
          List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
          if (parameterMappings != null) {
              // 这里根据 mapper.xml 配置的参数个数进行参数的设置
              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)) { 
                          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 将 javaType 转换为 jdbcType
                          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);
                      }
                  }
              }
          }
      }
      
    • ResultSet: 结果映射器, 将数据库查询结果组装为实体类.
      @Override
      public List<Object> handleResultSets(Statement stmt) throws SQLException {
          ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
      
          final List<Object> multipleResults = new ArrayList<Object>();
      
          int resultSetCount = 0;
          ResultSetWrapper rsw = getFirstResultSet(stmt);
      
          List<ResultMap> resultMaps = mappedStatement.getResultMaps();
          int resultMapCount = resultMaps.size();
          validateResultMapsCount(rsw, resultMapCount);
          while (rsw != null && resultMapCount > resultSetCount) {
              ResultMap resultMap = resultMaps.get(resultSetCount);
              handleResultSet(rsw, resultMap, multipleResults, null);
              rsw = getNextResultSet(stmt);
              cleanUpAfterHandlingResultSet();
              resultSetCount++;
          }
      
          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);
      }
      
  5. 参考:
    [1] : Java EE互联网轻量级框架整合开发
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!