Mybatis源码(二)mybatis核心组件介绍

Mybatis源码(二)mybatis核心组件

Configuration:用于描述mybatis的主配置信息,其他组件需要获取配置信息时,直接通过Configuration对象获取

MappedStatement:用于描述Mapper中的SQL的配置信息,是对MapperXML配置文件中select|update|delete|insert语句的封装

SqlSession:SqlSession是Mybatis提供的面向用户的API,表示和数据库交互时的会话对象。用于完成数据库的增删改查功能。SqlSession是Executor组件的外观。目的是对外提供易于理解和使用的操作数据库的接口

Executor:Executor是操作数据库的执行器。Mybatis中所有的增删改查操作都是由该执行器完成的

StatementHandler:StatementHandler是对Statement对象的封装。用于为Statement对象设置参数。调用statement接口提供的方法和数据库进行交互

parameterHandler: 当mybatis框架使用的statement类型为CallableStatement和prepareStatement时,为他们参数占位符设置值

ResultHandler:ResultHandler用于对JDBC的操作中ResultSet对象操作。执行SQL类型为SELECT语句时,ResultSetHandler用于将查询结果转换为Java对象

TypeHandler:Mybatis中的类型处理器。用于处理java类型和jdbc类型之间的映射

1.Configuration

  1.Mybatis启动后,会对所有的配置信息进行解析,然后将解析后的内容注册到Configuration对象中。

  2.Configuration对象还将作为Executor和StatementHandler和ResultSetHandler和ParameterHandler组件的工厂类

  3.Configuration类中提供了用于控制Mybatis的属性行为配置和作为容器存放TypeHandler和TypeAlias和Mapper接口和MapperSQL的配置信息

2.Executor

  SqlSession是mybatis提供的操作数据库的api,但是真正执行的还是Executor

  Executor执行器分类

    SimpleExecutor:基础的执行器,能完成增删改查操作

    BatchExecutor:用于批量操作

    ResueExecutor:对JDBC的statement对象做了缓存。当执行相同的sql语句时,直接从缓存中取出statement对象进行复用避免频繁的创建和销毁对象。享元模式的思想

  代码使用案例

public class ExecutorExample {

    @Before
    public void initData() {
        DbUtils.initData();
    }

    @Test
    public void testExecutor() throws IOException, SQLException {
        // 获取配置文件输入流
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        // 通过SqlSessionFactoryBuilder的build()方法创建SqlSessionFactory实例
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 调用openSession()方法创建SqlSession实例
        SqlSession sqlSession = sqlSessionFactory.openSession();
        Configuration configuration = sqlSession.getConfiguration();
        // 从Configuration对象中获取描述SQL配置的MappedStatement对象
        MappedStatement listAllUserStmt = configuration.getMappedStatement(
                "com.blog4java.mybatis.com.blog4java.mybatis.example.mapper.UserMapper.listAllUser");
        //创建ReuseExecutor实例
        Executor reuseExecutor = configuration.newExecutor(
                new JdbcTransaction(sqlSession.getConnection()),
                ExecutorType.REUSE
        );
        // 调用query()方法执行查询操作
        List<UserEntity> userList = reuseExecutor.query(listAllUserStmt,
                null,
                RowBounds.DEFAULT,
                Executor.NO_RESULT_HANDLER);
        System.out.println(JSON.toJSON(userList));
    }
}
View Code

3.MapperStatement

  用于描述Mapper中的SQL的配置信息,是对MapperXML配置文件中select|update|delete|insert语句的封装

4.StatementHandler

  StatementHandler是对Statement对象的封装。用于为Statement对象设置参数。调用statement接口提供的方法和数据库进行交互

  StatementHandler实现类有SimpleStatementHandler、RoutingStatementHandler、CallableStatementHandler、PreparedStatementHandler

5.ParameterHandler

  作用:在preparedStatementHandler和CallableStatementHandler操作数据数据库之前为参数占位符设置值

6.ResultSetHandler

  ResultSetHandler用于在StatementHandler对象执行完查询操作后或者存储过程后,对结果集或者存储过程执行后续处理

  ResultSetHandler是一个接口,只有一个默认的实现类 DefaultResultSetHandler

  ResultSetHandler接口中有三个方法:

  <E> List<E> handleResultSets(Statement stmt) throws SQLException; 获取statement对象的resultSet对象。对ResultSet对象进行处理。返回包含结果实体的List对象
  
@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;
    // 1、获取ResultSet对象,將ResultSet对象包装为ResultSetWrapper
    ResultSetWrapper rsw = getFirstResultSet(stmt);
    // 2、获取ResultMap信息,一般只有一个ResultMap
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    // 校验ResultMap,如果该ResultMap名称没有配置,则抛出异常
    validateResultMapsCount(rsw, resultMapCount);
    // 如果指定了多个ResultMap,则对每个ResultMap进行处理
    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      // 3、调用handleResultSet方法处理结果集
      handleResultSet(rsw, resultMap, multipleResults, null);
      // 获取下一个结果集对象,需要JDBC驱动支持多结果集
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }
    // 如果JDBC驱动支持多结果集,可以通过<select>标签resultSets属性指定多个ResultMap
    // 处理<select>标签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方法处理结果集
          handleResultSet(rsw, resultMap, null, parentMapping);
        }
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
      }
    }
    // 对multipleResults进行处理,如果只有一个结果集,则返回结果集中的元素,否则返回多个结果集
    return collapseSingleResultList(multipleResults);
  }
View Code
  <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;将ResultSet对象包装成Cursor对象。对Cursor对象进行遍历时,能够动态的从数据库查询数据,避免一次性将所有的数据加载到内存中
  @Override
  public <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling cursor results").object(mappedStatement.getId());

    ResultSetWrapper rsw = getFirstResultSet(stmt);

    List<ResultMap> resultMaps = mappedStatement.getResultMaps();

    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    if (resultMapCount != 1) {
      throw new ExecutorException("Cursor results cannot be mapped to multiple resultMaps");
    }

    ResultMap resultMap = resultMaps.get(0);
    return new DefaultCursor<E>(this, resultMap, rsw, rowBounds);
  }
View Code
  void handleOutputParameters(CallableStatement cs) throws SQLException; 处理存储过程调用结果
  // 处理存储过程Output参数
  @Override
  public void handleOutputParameters(CallableStatement cs) throws SQLException {
    final Object parameterObject = parameterHandler.getParameterObject();
    final MetaObject metaParam = configuration.newMetaObject(parameterObject);
    final List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    for (int i = 0; i < parameterMappings.size(); i++) {
      final ParameterMapping parameterMapping = parameterMappings.get(i);
      if (parameterMapping.getMode() == ParameterMode.OUT || parameterMapping.getMode() == ParameterMode.INOUT) {
        if (ResultSet.class.equals(parameterMapping.getJavaType())) {
          handleRefCursorOutputParameter((ResultSet) cs.getObject(i + 1), parameterMapping, metaParam);
        } else {
          final TypeHandler<?> typeHandler = parameterMapping.getTypeHandler();
          metaParam.setValue(parameterMapping.getProperty(), typeHandler.getResult(cs, i + 1));
        }
      }
    }
  }
View Code

7.TypeHanadler

  Mybatis通过TypeHandlerRegistry建立JDBC类型、java类型与TypeHandler之间的转换关系

  TypeHandlerRegister通过map对象保存JDBC类型、java类型和handler之间的关系

  如果自定义了类型转换器,也可以通过TypeHandlerRegistry类额register()方法进行注册

  部分代码片段如下:

  

public final class TypeHandlerRegistry {

  private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap<JdbcType, TypeHandler<?>>(JdbcType.class);
  private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new ConcurrentHashMap<Type, Map<JdbcType, TypeHandler<?>>>();
  private final TypeHandler<Object> UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler(this);
  private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap<Class<?>, TypeHandler<?>>();

  private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = Collections.emptyMap();

  private Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class;

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

    register(String.class, JdbcType.SQLXML, new SqlxmlTypeHandler());

    // mybatis-typehandlers-jsr310
    if (Jdk.dateAndTimeApiExists) {
      this.register(Instant.class, InstantTypeHandler.class);
      this.register(LocalDateTime.class, LocalDateTimeTypeHandler.class);
      this.register(LocalDate.class, LocalDateTypeHandler.class);
      this.register(LocalTime.class, LocalTimeTypeHandler.class);
      this.register(OffsetDateTime.class, OffsetDateTimeTypeHandler.class);
      this.register(OffsetTime.class, OffsetTimeTypeHandler.class);
      this.register(ZonedDateTime.class, ZonedDateTimeTypeHandler.class);
      this.register(Month.class, MonthTypeHandler.class);
      this.register(Year.class, YearTypeHandler.class);
      this.register(YearMonth.class, YearMonthTypeHandler.class);
      this.register(JapaneseDate.class, JapaneseDateTypeHandler.class);
    }

    // issue #273
    register(Character.class, new CharacterTypeHandler());
    register(char.class, new CharacterTypeHandler());
  }

8.SqlSession

  SqlSession是Mybatis提供的面向用户的API,表示和数据库交互时的会话对象。用于完成数据库的增删改查功能。SqlSession是Executor组件的外观。目的是对外提供易于理解和使用的操作数据库的接口

原文地址:https://www.cnblogs.com/yingxiaocao/p/13550001.html