什么!你不知道Mybatis的核心组件?

196 阅读5分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情

在学习一个框架前,我们不能只看到他的表象,也得知道他的内在结构,今天介绍下他的核心组件

SqlSessionFactoryBuilder

SqlSessionFactory的构造器,根据配置信息或代码配置生成SqlSessionFactory。对于这个对象的使用,一般new SqlSessionFactoryBuilder(),就直接使用这个对象构造 SqlSessionFactory。

源码(篇幅有限,只列举主要代码)

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
  try {
    // 解析mybatis配置文件
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
    return build(parser.parse());
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error building SqlSession.", e);
  } finally {
    ErrorContext.instance().reset();
    try {
      inputStream.close();
    } catch (IOException e) {
      // Intentionally ignore. Prefer previous error.
    }
  }
}

/**
 * 真正创建SqlSessionFactory方法
 *
 * 调用来源
 * 1.通过解析xml配置文件获取信息并填充Configuration配置类中
 * 2.后面mybatis-spring框架通过解析配置注解并填充Configuration配置类中
 *
 * @param config
 * @return
 */
public SqlSessionFactory build(Configuration config) {
  // 传入配置类通过构造方法创建默认的SqlSessionFactory
  return new DefaultSqlSessionFactory(config);
}

通过源码可以看出,这个类主要是通过获取一个全文的配置类,通过构造方法创建一个默认的SqlSessionFactory。使用的是建造者模式。

SqlSessionFactory

生成SqlSession的工厂接口

SqlSessionFactory接口有两个实现类

  • DefaultSqlSessionFactory:默认的实现类
  • SqlSessionManager:暂时没有进行使用

源码(篇幅有限,只列举主要代码)

代码比较简单,就不注释了

@Override
public SqlSession openSession() {
  return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}

@Override
public SqlSession openSession(boolean autoCommit) {
  return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);
}

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
  Transaction tx = null;
  try {
    final Environment environment = configuration.getEnvironment();
    final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
    tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
    final Executor executor = configuration.newExecutor(tx, execType);
    return new DefaultSqlSession(configuration, executor, autoCommit);
  } catch (Exception e) {
    closeTransaction(tx); // may have fetched a connection so lets call close()
    throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}

构建SqlSessionFactory的方式

使用XML

就是创建mybatis.xml进行一系列的配置,太古老,就不演示了

在程序中使用代码

    // 不推荐使用代码方式,要设置很多配置属性,目前mybatis-spring已经实现注解加配置去实现了
    @Bean
    public SqlSessionFactory sqlSessionFactory2(){
        // 构建数据库连接池
        PooledDataSource pooledDataSource = new PooledDataSource();
        pooledDataSource.setDriver("com.mysql.cj.jdbc.Driver");
        pooledDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/mybatis_study?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai");
        pooledDataSource.setUsername("root");
        pooledDataSource.setPassword("root");

        // 事务管理工厂
        TransactionFactory transactionFactory = new JdbcTransactionFactory();

        // 数据库运行环境
        Environment environment = new Environment("dev", transactionFactory, pooledDataSource);

        // 构建配置对象
        Configuration configuration = new Configuration(environment);

        // 设置数据库属性映射别名
        configuration.getTypeAliasRegistry().registerAlias("userMapper",UserMapper.class);

        // 添加mapper映射文件
        configuration.addMapper(UserMapper.class);

        return new SqlSessionFactoryBuilder().build(configuration);
    }

mybatis-spring框架的实现

image.png

先留个截图,之后有机会在spring专栏再说

SqlSession

sql执行的会话接口

从上面的源码,我们能看到SqlSession使用的实现类是DefaultSqlSession

那么SqlSession怎么就说是一个sql会话接口呢,我们看看源码

源码(篇幅有限,只列举主要代码)

/**
 * 通过接口的class文件名获取接口的代理对象
 * @param type Mapper interface class
 * @param <T>
 * @return
 */
@Override
public <T> T getMapper(Class<T> type) {
  return configuration.<T>getMapper(type, this);
}

截取一个查询为例

/**
 *
 * @param statement 执行sql的id,例:statement = com.wood.mybais.mapper.UserMapper.query
 * @param parameter 参数对象
 * @param handler 结果处理器
 */
@Override
public void select(String statement, Object parameter, ResultHandler handler) {
  select(statement, parameter, RowBounds.DEFAULT, handler);
}

/**
 *
 * @param statement
 * @param parameter
 * @param rowBounds RowBound instance to limit the query results
 * @param handler
 */
@Override
public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
  try {
    // 通过sql的id获取映射的关系对象
    MappedStatement ms = configuration.getMappedStatement(statement);
    // executor通过构造方法注入
    // 执行器去执行sql
    executor.query(ms, wrapCollection(parameter), rowBounds, handler);
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}

注:Executor内部会获取数据库连接,然后执行真正的数据库交互工作

主要用途

由上面的源码不难看出,SqlSession有以下作用:

  1. 获取映射器。让映射器通过命名空间接口名称找到对应的sql,然后发送到数据库并返回结果
  2. 直接通过命令信息去执行sql返回结果。SqlSession就有update,insert等方法,带上sql的id操作XML配置好的sql去执行;并且他还支持事务,通过commit和rollback提交或回滚事务。

SqlMapper

映射器,由Java接口和XML文件(或者注解)共同组成

作用

  • 定义参数类型
  • 描述缓存
  • 描述SQL语句
  • 定义查询结果和对象的映射关系

实现方式

  • XML配置文件

Java接口+SQL配置文件

  • 注解

Java接口+注解

生命周期

  • SqlSessionFactoryBuilder

作用就是构建SqlSessionFactory,构建完就失去了存在的意义。所以他的生命周期存在于方法的局部

  • SqlSessionFactory

    • 作用就是创建sqlsession,而每次程序访问数据库的时候都需要创建新的sqlsession,所以SqlSessionFactory应该存在于mybatis应用的整个生命周期中
    • 如果同一个数据库多次创建SqlSessionFactory,而每次创建SqlSessionFactory都会打开更多的数据库连接资源,造成资源浪费
    • 所以SqlSessionFactory应该存在于mybatis的整个生命周期中,并且采用单例模式
  • SqlSession

    • 数据库连接的一个会话,相当于JDBC的Connection对象。生命周期存在于应用对数据库的请求和操作中,每次创建后的SqlSession都必须要关闭它
  • Mapper

    • 就是一个接口,是个方法级别的东西。在被调用后他的生命周期就结束了,最大的声明周期和SqlSession一样,也就是SqlSession只执行了一条SQL语句。

总结

通过上面的介绍,是不是发现和JDBC的操作步骤有些类似。其实框架无非是对于JDBC的充分封装,屏蔽了很多的细节而已。

结语

觉得写的不错的xdm,可以点波小赞。

文章难免有疏漏,也欢迎xdm指正。

一起学习,一起成长!