前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MyBatis源码分析一:核心组件

MyBatis源码分析一:核心组件

作者头像
心平气和
发布2021-01-13 15:42:13
4080
发布2021-01-13 15:42:13
举报

一、整体介绍

前面一篇文章MyBatis3使用 讲解了MyBatis的基本使用,这篇介绍MyBatis的核心组件,让我们从整体上了解MyBatis的组成。

MyBatis核心类/接口如下:

Configuration(主配置类)

MappedStatement:描述Xml中Sql配置

SqlSession:用户使用最多的就是这个接口,用于执行Sql

Executor:Sql执行器,上面SqlSession就是调用Executor来完成Sql执行的 几个Handler:

StatementHandler:封装了JDBC Statement对象的操作

ParameterHandler:当Statement为PreparedStatement或CallableStatement时用于设置参数

ResultHandler:用于将JDBC的ResultSet转换为Java对象

TypeHandler:用于处理JDBC类型和Java类型的转换

二、Configuration

Configuration:描述主配置信息,对应前面讲的主配置文件,另外它还是其它组件的容器,像Mapper、TypeHandler等都会注册到这里。

在主配置文件中settings节点配置的一些属性在这里都有对应。

常用配置如下:

cacheEnabled:是否开启2级缓存,2级缓存基于Mapper。

lazyLoadingEnabled:延迟加载开关,主要是延迟加载一些关联对象,建议设为true。

multiResultSetEnabled:是否允许一语句返回多结果集,建议关闭。

defaultStatementTimeout:Statement的超时时间,建议开启为3-10秒。

defaultFetchSize:从数据库获取最大行数,建议设置1000以内,后台系统除外。

三、MappedStatement

描述一条Sql语句,如前面一篇文章举例的:

代码语言:javascript
复制
<select id="listAllUser"  resultType="com.liujh.entity.UserEntity" >
        select
        <include refid="userFields"/>
        from user
    </select>

select标签常用属性如下:

id:唯一标识

parameterType:参数的Java类型

resultType:描述结果和Java类的映射关系

timeout:超时时间

以下为例子

代码语言:javascript
复制
 <resultMap id="BaseResultMap" type="Post">
        <id column="id" property="id" jdbcType="INTEGER"/>
        <result column="title" property="title" jdbcType="VARCHAR"/>
        <result column="author" property="author" jdbcType="VARCHAR"/>
        <result column="create_time" property="createTime" jdbcType="BIGINT"/>
    </resultMap>

    <sql id="Table_Name">
        post1
    </sql>

    <select id="list" resultMap="BaseResultMap" parameterType="Post">
        select
        id,title,author,create_time
        from
        <include refid="Table_Name"/>
        <where>
            <if test="title !=null and title!=''">
                and title like CONCAT(#{title},'%')
            </if>
        </where>

        <if test="offset!=null and limit!=null and limit>0">
            limit #{offset},#{limit}
        </if>

表结构如下:

代码语言:javascript
复制
CREATE TABLE `post1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) DEFAULT NULL,
  `author` varchar(255) DEFAULT NULL,
  `create_time` bigint(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

四、SqlSession

这个是门面类,面向用户Api大部分接口是通过这个接口暴露出去的,我们看一段代码:

代码语言:javascript
复制
 // 获取配置文件输入流
  InputStream inputStream = Resources.getResourceAsStream("META-INF/spring/mybatis-config.xml");
  // 通过SqlSessionFactoryBuilder的build()方法创建SqlSessionFactory实例
  SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

  //构造数据源
  BoneCPDataSource dataSource= new BoneCPDataSource();
  dataSource.setDriverClass(driverClass);
  dataSource.setJdbcUrl(jdbcUrl);
  dataSource.setUsername(userNmae);
  dataSource.setPassword(password);

  String id = "SqlSessionFactoryBean";
  TransactionFactory transactionFactory = new SpringManagedTransactionFactory();
  Environment newEnv = new Environment(id, transactionFactory, dataSource);

  sqlSessionFactory.getConfiguration().setEnvironment(newEnv);
  // 调用openSession()方法创建SqlSession实例
  SqlSession sqlSession = sqlSessionFactory.openSession();
  // 获取UserMapper代理对象
  UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
  // 执行Mapper方法,获取执行结果
  List<UserEntity> userList = userMapper.listAllUser();

可以看到SqlSession由SqlSessionFactory打开,执行SQL先由SqlSession获取一个Mapper,参数为Mapper接口类,然后直接执行其方法就可以了,Mapper接口定义如下:

代码语言:javascript
复制
@Repository
public interface UserMapper {
    List<UserEntity> listAllUser();
}

五、Executor

真正执行Sql的组件,我们用的最多的是SimpleExecutor,还有加入缓存的CachingExecutor,以下为使用代码:

代码语言:javascript
复制
 public void testExecutor() throws IOException, SQLException {
    InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    Configuration configuration = sqlSession.getConfiguration();
    MappedStatement listAllUserStmt = configuration.getMappedStatement(
            "com.edward.mapper.UserMapper.listAllUser");
    Executor executor = configuration.newExecutor(
            new JdbcTransaction(sqlSession.getConnection()),
            ExecutorType.SIMPLE
    );
    List<UserEntity> userList = executor.query(listAllUserStmt,
            null,
            RowBounds.DEFAULT,
            Executor.NO_RESULT_HANDLER);
}

可以看到Executor由Configuration创建,query即查询方法需要一个MappedStatement参数(由Configuration.getMappedStatement获取)。

其中query方法定义在父类BaseExecutor中

代码语言:javascript
复制
@Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter);
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
 }

更新对应的update方法,这里不详述。

六、几个Handler

这几个不在主流程上,代码不是很复杂,可以根据自己情况查阅,挑一个ResultSetHandler讲下。

ResultSetHandler用于将JDBC返回的ResultSet转化成Java对象,看下定义

代码语言:javascript
复制
public interface ResultSetHandler {

  <E> List<E> handleResultSets(Statement stmt) throws SQLException;

  void handleOutputParameters(CallableStatement cs) throws SQLException;

}

其中handleOutputParameters用于处理存储过程输出参数,一般互联网公司不建议用存储过程,所以就跳过这个,看下handleResultSets。

代码语言:javascript
复制
public List<Object> handleResultSets(Statement stmt) throws SQLException {
    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++;
    }

其调用handleResultSet来处理单个结果集,最终又通过ResultHandler::handleResult来处理单字段:

代码语言:javascript
复制
@Override
  public void handleResult(ResultContext<? extends V> context) {
    final V value = context.getResultObject();
    final MetaObject mo = MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
    // TODO is that assignment always true?
    final K key = (K) mo.getValue(mapKey);
    mappedResults.put(key, value);
  }
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-01-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员升级之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档