Mybatis执行流程

461 阅读3分钟

这是我参与11月更文挑战的第9天,活动详情查看:2021最后一次更文挑战

我们在之前为了方便写了一获取SqlSession的工具类,我们今天来分析一下Mybatis的执行流程

String resource = "mybatsi-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student studentById = mapper.findStudentById(38);
System.out.println(studentById);
sqlSession.close();

这是我们根据id查询学生信息的代码,通过代码我们发现Myabtis的执行流程是

  1. 获取Mybatis主配置文件
  2. 通过SqlSessionFactoryBuilder().build()方法传入主配置文件构建SqlSessionFactory对象
  3. SqlSessionFactory对象通过openSession()获取SqlSession对象
  4. sqlSession.getMapper()方法获取我们xml文件得到mapper对象去执行我们的sql返回对象

到底具体怎么只执行,我们来看源码看都做了些什么

Mybatis的组件

SqlSessionFactoryBuilder

通过SqlSessionFactoryBuilder这个建造者通过XMLConfigBuilder()读取传入的inputStream的Mybatis主配置文件的流,XMLConfigBuilder.parse()解析主配置文件返回一个Configuration()对象,在调用一个build方法返回一个SqlSessionFactory()对象,还有两个参数SqlSessionFactory和properties,environment 决定加载哪种环境,包括数据源和事务管理器,比如我们的项目有 开发环境,UAT测试环境,在配置文件中<environments中有多个environment。properties是来加载一些属性的。

上边结束后我们返回一个SqlSessionFactory()对象

public SqlSessionFactory build(InputStream inputStream, String SqlSessionFactory, Properties properties) {
    SqlSessionFactory var5;
    try {
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
        var5 = this.build(parser.parse());
    } catch (Exception var14) {
        throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
    } finally {
        ErrorContext.instance().reset();

        try {
            inputStream.close();
        } catch (IOException var13) {
        }

    }

    return var5;
}

public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
}

SqlSessionFactory

有了SqlSessionFactory之后我们就可以tong过他来获取SqlSession对象,SqlSessionFactory重载的多个 openSession() 方法供使用,通过参数命名我们可以知道下边的方法参数是做什么的,我们默认的openSession()方法是没有参数的,会创建包含如下属性的SqlSession对象

  1. autoCommit是否自动提交
  2. Connection创建数据库会话
  3. TransactionIsolationLevel事务隔离级别
  4. ExecutorType执行器类型

ExecutorType有三种类型

  1. SIMPLE 该类型的执行器没有特别的行为。它为每个语句的执行创建一个新的预处理语句
  2. REUSE 复用预处理语句
  3. BATCH该类型的执行器会批量执行所有更新语句
public enum ExecutorType {
    SIMPLE,
    REUSE,
    BATCH;

    private ExecutorType() {
    }
}

还有一个getConfiguration方法,获取Configuration对象,也就是解析xml配置文件中的<configuration标签后的数据

public interface SqlSessionFactory {
    SqlSession openSession();

    SqlSession openSession(boolean var1);

    SqlSession openSession(Connection var1);

    SqlSession openSession(TransactionIsolationLevel var1);

    SqlSession openSession(ExecutorType var1);

    SqlSession openSession(ExecutorType var1, boolean var2);

    SqlSession openSession(ExecutorType var1, TransactionIsolationLevel var2);

    SqlSession openSession(ExecutorType var1, Connection var2);

    Configuration getConfiguration();
}

看我啊接口我们再去看SqlSessionFactory的实现类 DefaultSqlSessionFactory()每一个openSession方法都要调用openSessionFromDataSource去返回SqlSession对象

  1. 先获取我们主配置文件的environment包函数据库配置和事务信息,
  2. 创建TransactionFactory事务工厂,调用newTransaction方法传入,数据库信息,隔离级别,是否自动提交。
  3. 新建一个构造器executor传入事务,构造器类型
  4. 返回一个 DefaultSqlSession对象

这是我们就获得一个SqlSession对象,我们的sql语句都是executor在帮我们执行,excutor是对于Statement的封装

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;

    DefaultSqlSession var8;
    try {
        Environment environment = this.configuration.getEnvironment();
        TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        Executor executor = this.configuration.newExecutor(tx, execType);
        var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
    } catch (Exception var12) {
        this.closeTransaction(tx);
        throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);
    } finally {
        ErrorContext.instance().reset();
    }

    return var8;
}

SqlSession

SqlSession是Mybatis最强大的一个类,通过DefaultSqlSession的getMapper()来生成mapper的代理对象

SqlSessionManager.java

public <T> T getMapper(Class<T> type) {
    return this.getConfiguration().getMapper(type, this);
}

Configuration.java

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return this.mapperRegistry.getMapper(type, sqlSession);
}

MapperRegistry.java

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
    if (mapperProxyFactory == null) {
        throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    } else {
        try {
            return mapperProxyFactory.newInstance(sqlSession);
        } catch (Exception var5) {
            throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
        }
    }
}

MapperProxyFactory.java 动态代理我们的接口

protected T newInstance(MapperProxy<T> mapperProxy) {
    return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}

public T newInstance(SqlSession sqlSession) {
    MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
    return this.newInstance(mapperProxy);
}

这一通操作之后我们就拿到了代理对象去执行我们的方法,走的方法是MapperProxy的invoik方法

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        }

        if (method.isDefault()) {
            return this.invokeDefaultMethod(proxy, method, args);
        }
    } catch (Throwable var5) {
        throw ExceptionUtil.unwrapThrowable(var5);
    }

    MapperMethod mapperMethod = this.cachedMapperMethod(method);
    return mapperMethod.execute(this.sqlSession, args);
}

调用 mapperMethod.execute(this.sqlSession, args)判断是什么类型的sql,返回给sqlSession

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    Object param;
    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;
        } 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);
            if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) {
                result = Optional.ofNullable(result);
            }
        }
        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;
    }
}

我们现在看是如何实现getlist的

<E> List<E> selectList(String var1);
public <E> List<E> selectList(String statement) {
    return this.selectList(statement, (Object)null);
}

这里调用的.executor.query方法。可以看到有两个实现

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    List var5;
    try {
        MappedStatement ms = this.configuration.getMappedStatement(statement);
        var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception var9) {
        throw ExceptionFactory.wrapException("Error querying database.  Cause: " + var9, var9);
    } finally {
        ErrorContext.instance().reset();
    }

    return var5;
}

走CachingExecutor的query方法,先从二级缓存中获取,,没有的话走走另一个方法去一级缓存中那,没有的话 image.png

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter);
    CacheKey key = this.createCacheKey(ms, parameter, rowBounds, boundSql);
    return this.query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (this.closed) {
        throw new ExecutorException("Executor was closed.");
    } else {
        if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
            this.clearLocalCache();
        }

        List list;
        try {
            ++this.queryStack;
            list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
            if (list != null) {
                this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
            } else {
                list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
            }
        } finally {
            --this.queryStack;
        }

        if (this.queryStack == 0) {
            Iterator var8 = this.deferredLoads.iterator();

            while(var8.hasNext()) {
                BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)var8.next();
                deferredLoad.load();
            }

            this.deferredLoads.clear();
            if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
                this.clearLocalCache();
            }
        }

        return list;
    }
}

没有的话走queryFromDatabase方法去数据库查数据

private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);

    List list;
    try {
        list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
        this.localCache.removeObject(key);
    }

    this.localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
        this.localOutputParameterCache.putObject(key, parameter);
    }

    return list;
}

走doquery方法有四个实现,我们看SimpleExecutor的DoQuery方法

public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;

    List var9;
    try {
        Configuration configuration = ms.getConfiguration();
        StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
        stmt = this.prepareStatement(handler, ms.getStatementLog());
        var9 = handler.query(stmt, resultHandler);
    } finally {
        this.closeStatement(stmt);
    }

    return var9;
}

然后走StatementHandler的query方法,可以看到是statement去执行SQL语句了

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    String sql = this.boundSql.getSql();
    statement.execute(sql);
    return this.resultSetHandler.handleResultSets(statement);
}