mybatis执行流程原理

工作流程:

public static void main(String[] args) throws IOException {
    InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    List<User> userList = userMapper.getUserByUsername("admin");
}

1、获取配置文件mybatis-config.xml,由SqlSessionFactoryBuilder对象,读取并解析配置文件,返回SqlSessionFactory对象

2、由SqlSessionFactory创建SqlSession 对象,没有手动设置的话事务默认开启

3、调用SqlSession中的api,获取指定UserMapper.class文件

4、由jdk动态代理,获取到UserMapper.xml文件,内部进行复杂的处理,最后调用jdbc执行SQL语句,封装结果返回。

具体实现:

1.1、由SqlSessionFactoryBuilder读取xml配置文件,解析成Configuration对象

//在创建XMLConfigBuilder时,它的构造方法中解析器XPathParser已经读取了配置文件
public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    //parser是XPathParser解析器对象,读取节点内数据,<configuration>是MyBatis配置文件中的顶层标签
    parseConfiguration(parser.evalNode("/configuration"));
    //最后返回的是Configuration 对象
    return configuration;
}

//此方法中读取了各个标签内容并封装到Configuration中的属性中。
private void parseConfiguration(XNode root) {
    try {
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      loadCustomLogImpl(settings);
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
}

1.2、根据Configuration创建SqlSessionFactory

public SqlSessionFactory build(Configuration config) {
	//创建了DefaultSqlSessionFactory对象,传入Configuration对象。
    return new DefaultSqlSessionFactory(config);
  }

2.1、根据SqlSessionFactory创建会话DefaultSqlSession对象

// 创建SqlSession会话
public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}

/** 
 * 
 * ExecutorType:执行器的类型
 * level:事务级别
 * 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();
    }
}

2.2、事务创建:

// getTransactionFactoryFromEnvironment,根据配置的数据源驱动获取相对应的事务
private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment){
    if (environment == null || environment.getTransactionFactory() == null) {
        return new ManagedTransactionFactory();
    }
    return environment.getTransactionFactory();
}
// 比如配置mysql数据源,在初始化Configuration时,指定了jdbc事务工厂。
public Configuration() {
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    // ....省略N个配置项
}


// transactionFactory.newTransaction创建事务,存储连接、数据源、事务级别等信息
public enum TransactionIsolationLevel {
 	NONE(Connection.TRANSACTION_NONE),	// 无事务
    READ_COMMITTED(Connection.TRANSACTION_READ_COMMITTED), // 不可重复读
    READ_UNCOMMITTED(Connection.TRANSACTION_READ_UNCOMMITTED),	// 读未提交
    REPEATABLE_READ(Connection.TRANSACTION_REPEATABLE_READ),	// 可重复读
    SERIALIZABLE(Connection.TRANSACTION_SERIALIZABLE);	// 串行化
}

2.3、执行机创建

// 根据事务和执行机类型创建执行机(默认不指定则为SimpleExecutor)
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
        executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
        executor = new ReuseExecutor(this, transaction);
    } else {
        executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
        executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
}

// 执行机加入到每个拦截器executor = (Executor) interceptorChain.pluginAll(executor);
// 说明这个执行器需要被各个拦截器拦截处理
private final List<Interceptor> interceptors = new ArrayList<>(); // 所有的拦截器
public Object pluginAll(Object target) {// target执行机
    for (Interceptor interceptor : interceptors) {
        target = interceptor.plugin(target);
    }
    return target;
}

2.3.1、执行机区别

a、SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象    
b、ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map内,供下一次使用。简言之,就是重复使用Statement对象  

c、BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中addBatch(),等待统一执行executeBatch(),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同
作用范围:SqlSession生命周期范围内

d、CachingExecutor:默认开启,同个作用域下的缓存执行机,使用内存的,将数据保存到缓存中,这样可以有效的解决增删改查性能

2.3.2、拦截器

// 拦截器可以自定义,但是只能定义以下几种类,因为Configuration只加载这几类
// 默认的拦截器顺序:Executor -> ParameterHandler -> StatementHandler -> ResultSetHandler
// 第一种:源码对ParameterHandler类的处理,对参数的拦截
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
    return parameterHandler;
}
// 第二种:源码对ResultSetHandler类的处理,对结果集的拦截,可以对返回的结果进行处理
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
                                            ResultHandler resultHandler, BoundSql boundSql) {
    ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
    resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
    return resultSetHandler;
}
// 第三种:源码对StatementHandler类的处理,对查询前的拦截,可以对最终sql作修改
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
}

自定义实现拦截器

@Intercepts({@Signature(
    type = StatementHandler.class, 
    method = "prepare", 
    args = {Connection.class, Integer.class})})
public class SqlStatusInterceptor implements Interceptor {// 实现接口方法}

2.4、创建DefaultSqlSession

// 默认为DefaultSqlSession,顶级接口SqlSession,作用是操作sql命令、获取mapper和管理事务
new DefaultSqlSession(configuration, executor, autoCommit);

3.1、根据jdk动态代理获取指定Mapper

UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

// 会根据mapperRegistry这个map获取对应的UserMapper.xml
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
}
// 而mapperRegistry内部的关键代码
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    // ....
	mapperProxyFactory.newInstance(sqlSession);
    // ....
}
// mapperProxyFactory.newInstance(sqlSession); 
// 此处内部由MapperProxyFactory创建jdk的代理对象(Proxy对象),
protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
// 而knownMappers对象则是在加载配置文件的时候动态addMapper进去的
public void addMappers(String packageName, Class<?> superType) {
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
    Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
    for (Class<?> mapperClass : mapperSet) {
        addMapper(mapperClass);
    }
}

至此,根据SqlSession,可以由getMapper获取指定的代理对象。

而代理对象怎么调用sql命令呢?原理则是根据MappedStatement对象。

4.1、根据代理对象查询

List<User> userList = userMapper.getUserByUsername("admin");

// 该代理对象先获取MappedStatement对象,获取UserMapper.xml信息
// 根据该xml信息去执行相应的sql语句
@Override
public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
    // ...
    MappedStatement ms = configuration.getMappedStatement(statement);
    executor.query(ms, wrapCollection(parameter), rowBounds, handler);
    // ...
}

4.2、MappedStatement对象

protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")
      .conflictMessageProducer((savedValue, targetValue) ->
          ". please check " + savedValue.getResource() + " and " + targetValue.getResource());

// 该对象的值是在初始化配置文件时加载的
// 过程:XMLConfigBuilder->XMLMapperBuilder->XMLStatementBuilder->MapperBuilderAssistant
// key:全包名,value:
private void parseConfiguration(XNode root) {
    // ...
    mapperElement(root.evalNode("mappers"));
    // ...
}
// 解析UserMapper.xml的mapper元素
private void mapperElement(XNode parent) throws Exception {
    // ...
    for (XNode child : parent.getChildren()) {
        XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
    }
    // ...
}
// 解析每个节点
public void parseStatementNode() {
    // ...
    builderAssistant.addMappedStatement(...);
    // ...
}
// 增加到MappedStatement对象中
public MappedStatement addMappedStatement(...) {
    MappedStatement.Builder statementBuilder = new MappedStatement.Builder(...);
    ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
    if (statementParameterMap != null) {
      statementBuilder.parameterMap(statementParameterMap);
    }
    MappedStatement statement = statementBuilder.build();
    // 关键代码
    configuration.addMappedStatement(statement);
    return statement;
}

4.3、Excutor对象执行查询

获取了MappedStatement对象,则由执行机将该对象xml信息转换成BoundSql(最终SQL语句),继而执行sql命令

@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
// query方法内部调用-->queryFromDatabase方法内部调用-->(SimpleExcutor)doQuery方法为jdbc实现
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
        Configuration configuration = ms.getConfiguration();
        StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
        stmt = prepareStatement(handler, ms.getStatementLog());
        return handler.query(stmt, resultHandler);
    } finally {
        closeStatement(stmt);
    }
} 

5、总结(核心代码总结)

1、获取配置文件mybatis-config.xml

2、由SqlSessionFactoryBuilder对象,读取配置文件

3、根据配置文件设置MapperRegistry对象(存放接口代理对象)

4、根据配置文件设置MappedStatement对象(存放接口对应的xml信息)

5、根据配置文件解析TransactionFactory事务对象(如mysql为JdbcTransactionFactory)

6、根据配置文件解析Excutor对象(默认SimpleExcutor执行机)

7、将Excutor加入拦截器(拦截器顺序Executor\ParameterHandler\StatementHandler\ResultSetHandler)

8、返回SqlSessionFactory对象,由SqlSessionFactory创建SqlSession 对象

9、调用SqlSession中的api,获取指定的接口代理对象(根据MapperRegistry对象获取)

3、调用SqlSession中的api,获取指定UserMapper.class文件

10、该代理对象调用查询方法,由拦截器拦截,获取指定的接口对应xml信息,组装BoundSql(SQL语句)对象

11、根据BoundSql对象,获取SQL语句,由驱动(如mysql为jdbc)调用sql命令,查询数据库。

参考资料:

https://blog.csdn.net/weixin_43184769/article/details/91126687

https://zhuanlan.zhihu.com/p/59844703

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章