MyBatis源码

346 阅读7分钟

Executor

基本介绍

  • 每一个SqlSession对象都会被分配一个执行器对象。
  • 执行器对象主要负责Connection对象的获取和Statement对象的管理方案。

Statement对象的管理方案

  • 1)简单方案:一个Statemnt接口对象只执行一次,执行完毕就会将Stament接口对象销毁。---》SimpleExecutor【简单执行器】

  • 2)可重用方案:使用一个Map集合,关键字就是一条Sql语句,对应的value就是Statemnt接口对象---》ReuseExecutor【可重用执行器】,等到SqlSession再次接收到相同的SQL命令时,就会从map集合中找到对象的Statement接口使用。map.put("select * from order",statement1)

  • 3)批处理管理方案:将多个Statement包含的SQL语句,交给一个Statement对象输送到数据库中,相乘批处理操作。--》BatchExecutor【批处理执行器】

Executor继承结构

image.png

Executor接口一共有两个实现类:

  • BaseExecutor:他是一个抽象类,减轻Executor接口的实现难度,这里就用到了适配器设计模式。
  • CachingExecutor【缓存执行器】:当接收到查询任务时,该执行器先去缓存中查找是否有该铲鲟对象的结果,如果有就直接返回,否则就交给其他的执行器执行,即BaseExecutor的实现类去执行。

注意:MyBatis默认情况下都会配置一个缓存执行器,来提高查询效率的,并且其中delegate变量存储另外一个执行器对象

BaseExecutor抽象类的子类:

  • 1)SimpleExecutor【简单执行器】:默认执行器
  • 2)ReuseExecutor【可重用执行器】
  • 3)BatchExecutor【批处理执行器】

Excecutor对象创建

执行器对象是由 Coniguration 对象负责创建的 .Configuration 对象会根据得到 [ExecutorType] 创建对应的 Excecutor 对象 , 并把这个 Excecutor 对象传给 SqlSession 对象

image.png

image.png

image.png

image.png

ExcecutorType的选择

ExecutorType来决定Configuration对象创建何种类型的执行器,它的赋值可以通过两个地方进行赋值,首先 可以通过SqlMapConfig.xml配置文件中的settings标签来设置当前工程中所有 SqlSession对象使用的默认Executour,如下图所示

image.png

也可以通过SqlSessoinFactory中openSession方法来指定具体的SqlSession使用的执行器。

image.png

StatementHandler

基本介绍

  • 主要负责【Statement或者PreparedStatement或者CallableStatement】创建工作,
  • 同时负责【PreparedStatement或者CallableStatement】运输的SQL语句中占位符的赋值任务,
  • 此外还负责数据库操作对象的行为,是执行executeUpdate更新【update】?还是执行executeQuery查询【query】。
public interface StatementHandler {

    Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException;

    
    void parameterize(Statement statement) throws SQLException;

    
    void batch(Statement statement) throws SQLException;

   
    int update(Statement statement) throws SQLException;

    
    <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException;
}
  • 1.prepare():创建Statement对象或者PreparedStatement或者CallableStatement。
  • 2.parameterize:主要针对PreparedStatement或者CallableStatement关联的预编译SQL语句中占位符的赋值操作,该方法是通过ParameterHandler中的setParameter方法给参数赋值的。
  • 3.update:主要负责数据库操作对象中的更新行为,即执行executeUpdate。
  • 4.query:主要负责数据库操作对象中的查询行为,即执行executeQuery。

StatementHandler继承结构

image.png

该接口一共有两个实现类BaseStatementHandler【抽象类,这里又用到适配器设计模式】,RoutingStatementHandler

1.BaseStatementHandler一共有三个子类,即分别针对三个数据库操作对象

  • SimpleStatementHandler----->针对Statement对象进行初始化,因为创建方法在BaseStatementHandler中
  • PreparedStatementHandler->针对PreparedStatement对象初始化,因为创建方法在BaseStatementHandler中
  • CallableStatementHandler-->针对CallableStatement对象初始化,因为创建方法在BaseStatementHandler中

注意:这三个子类中没有prepare()方法,该方法是创建数据库操作对象的,该方法在它们的父类BaseStatementHandler类中【适配器设计模式的体现】,该类中prepare()方法决定要使用哪个数据库操作对象,是使用Statement?还是使用PreparedStatement?还是使用CallableStatement?该方法又通过instantiateStatement方法来创建数据库操作对象,instantiateStatement方法交给BaseStatementHandler中的三个子类来实现的

2.RoutingStatementHandler:相当于SimpleStatementHandler,PreparedStatementHandler以及CallableStatementHandler的调度器。RoutingStatementHandler构造方法,将会根据Executor的类型决定创建SimpleStatementHandler,PreparedStatementHandler以及CallableStatementHandler

注意:StatementHandler对象是在SqlSession对象接收到命令时,由Configuration中的newStatementHandler方法创建的。

StatementHandler对象创建

StatementHandler对象是在SqlSession对象接收到操作命令时, 由Configuraion中newStatementHandler 方法负责调用的。

image.png RoutingStatementHandler构造方法 , 将会根据Executor的类型决定创建SimpleStatementHandler,PreparedStatementHandler,CallableStatementHandler 实例对象。

image.png

StatementHandler接口方法介绍

prepare方法

image.png prepare方法用于创建一个 (Statement or PreparedStatement or CallableStatement) 对象 , 并设置 Statement 对象的最大工作时间和一次性读取的最大数据量让后将生成的 Statement 对象返回

prepare 方法只在BaseStatementHandler被实现 在其三个子类中没有被重写 用于三个子类调用获得对应的 Statement接口对象 ,prepare 方法依靠instantiateStatement(connection)方法来返回具体Statement接口对象 ,这个方法是BaseStatementHandle中定义的抽象方法 , 由三个子类来具体实现

SimpleHandler中的instantiateStatement方法如下:

image.png

PreparedStatementHandler中的instantiateStatement方法如下:

image.png

CallableStatementHandler中的instantiateStatement方法如下:

image.png

parameterize方法

主要为PreparedStatement和CallableStatement传参.因此只在PreparedStatementHandler和CallableStatementHandler中被重写。即设置SQL占位符的

PreparedStatementHandler中的parameterize

image.png

CallableStatementHandler中的parameterize

image.png

在这两个方法中,可以看到都是ParameterHandler对象进行参数赋值的.

query方法

输送查询查询语句,并将查询结果转换对应的实体类对象

SimpleStatementHandler 中的 query方法

image.png

PreparedStatementHandler中的query方法

image.png

CallableStatementHandler中的query方法

image.png 可以看到在得到查询结果后,都是使用ResultSetHandler对结果进行转换.

update方法

输送insert,update,delete语句并返回处理数据行

SimpleStatementHandler中的update方法

image.png PreparedStatementHandler中update方法

image.png

CallableStatementHandler中update方法

image.png

ParameterHandler

基本介绍

接口的作用是为了【PreparedStatement】和【CallableStatement】的输送的SQL语句中占位符进行赋值操作。

image.png

1)Object getParameterObject():读取开发人员提供参数内容(占位符对应的参数值)

2)setParameters(PreparedStatement ps):【通过反射机制】负责将参数内容设置到数据库操作对象中输送的sql语句的占位符上,例如ps.setInt(占位符的位置,参数的内容);在DefaultParameterHandler类中的setParameters方法最重要的代码typeHandler.setParameter(ps, i + 1, value, jdbcType);【通过改行代码进行参数占位符赋值的】,看源码是可以看看TypeHandler的具体实现类中的setParameter方法,例如IntegerTypeHandler中的该方法,熟悉代码ps.setInt(i,parameter);

ParameterHandler继承结构

只有一个实现类DefaultParameterHandler

image.png

ParameterHandler对象创建

参数处理器对象是在创建StatementHandler对象同时被创建的.由Configuration对象负责创建.

protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    // 获得 Configuration 对象
    this.configuration = mappedStatement.getConfiguration();

    this.executor = executor;
    this.mappedStatement = mappedStatement;
    this.rowBounds = rowBounds;

    // 获得 TypeHandlerRegistry 和 ObjectFactory 对象
    this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
    this.objectFactory = configuration.getObjectFactory();

    // 如果 boundSql 非空,一般是写类操作,例如:insert、update、delete ,则先获得自增主键,然后再创建 BoundSql 对象
    if (boundSql == null) { // issue #435, get the key before calculating the statement
        // 获得自增主键
        generateKeys(parameterObject);
        // 创建 BoundSql 对象
        boundSql = mappedStatement.getBoundSql(parameterObject);
    }
    this.boundSql = boundSql;

    // 创建 ParameterHandler 对象
    this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
    // 创建 ResultSetHandler 对象
    this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}

ResultSetHandler

基本介绍

将当前对数据库操作的结果映射为一个Java对象

image.png

ResultSetHandler接口的主要方法只有一个:handleResultSets方法【将来想要拦截时就是拦截该方法】

继承结构

只有一个实现类DefaultResultSetHandler

Mybatis源码剖析

解析配置文件

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

创建SqlSession对象

image.png

image.png

image.png

image.png

image.png

image.png

image.png

MapperProxy创建dao接口代理对象

image.png

image.png

image.png

image.png

image.png

protected T newInstance(MapperProxy<T> mapperProxy) {
    //第一个参数为类加载器,第二个参数为被代理类的接口数据,第三个参数为InvocationHandler的实现类
    return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}

image.png

MapperProxy中的Invoke方法

image.png

image.png

public Object execute(SqlSession sqlSession, Object[] args) {
    Object param;
    Object result;
    //根据dao接口方法类型执行不同的逻辑,比如查询就是SELECT
    switch(this.command.getType()) {
    case INSERT:
        param = this.method.convertArgsToSqlCommandParam(args);
        result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
        break;
    case UPDATE:
        param = this.method.convertArgsToSqlCommandParam(args);
        result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
        break;
    case DELETE:
        param = this.method.convertArgsToSqlCommandParam(args);
        result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
        break;
    case SELECT:
        if (this.method.returnsVoid() && this.method.hasResultHandler()) {
            this.executeWithResultHandler(sqlSession, args);
            result = null;
        //由于findAll方法的返回值是List,所以会执行下面的方法
        } else if (this.method.returnsMany()) {
            //查看该方法
            result = this.executeForMany(sqlSession, args);
        } else if (this.method.returnsMap()) {
            result = this.executeForMap(sqlSession, args);
        } else if (this.method.returnsCursor()) {
            result = this.executeForCursor(sqlSession, args);
        } else {
            param = this.method.convertArgsToSqlCommandParam(args);
            result = sqlSession.selectOne(this.command.getName(), param);
        }
        break;
    case FLUSH:
        result = sqlSession.flushStatements();
        break;
    default:
        throw new BindingException("Unknown execution method for: " + this.command.getName());
    }

    if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
        throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
    } else {
        return result;
    }
}

image.png

image.png

image.png

image.png

image.png

image.png

创建StatementHandler对象

image.png

image.png

image.png

image.png

创建ParameterHandler对象和ResultSetHandler对象

image.png

image.png

image.png

创建数据库操作对象

image.png

image.png

image.png

image.png

protected Statement instantiateStatement(Connection connection) throws SQLException {
    //创建数据库操作对象Statement对象
    return this.mappedStatement.getResultSetType() != null ? connection.createStatement(this.mappedStatement.getResultSetType().getValue(), 1007) : connection.createStatement();
}

执行数据库操作

image.png

image.png

image.png