• 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组件的外观。目的是对外提供易于理解和使用的操作数据库的接口

  • 相关阅读:
    SqlDependency和SqlCacheDependency的若干说明
    sublime 3 随笔
    [有得]解决redmine写操作很慢的问题
    Java双重循环
    使用 Docker 打包 Rust Web 服务
    Centos8.3、hadoop-2.6.4 简单的日志分析实验
    广域网数据交换的三种方式
    计算机的起源与发展
    推荐两款生成数据库表结构说明文档工具
    Centos8.3、docker部署springboot项目实战记录
  • 原文地址:https://www.cnblogs.com/yingxiaocao/p/13550001.html
Copyright © 2020-2023  润新知