Mybatis执行流程源码分析

一、SqlSessionFactoryBuilder构建

该方法用于创建SqlSessionFactory,包含下面几种类型的方法,用于创建SqlSessionFactory

  1. public SqlSessionFactory build(InputStream inputStream)
    
  2. public SqlSessionFactory build(InputStream inputStream, String environment)
    
  3. public SqlSessionFactory build(InputStream inputStream, Properties properties)
    
  4. public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties)
    
  5. public SqlSessionFactory build(Configuration config)
    
  6. public SqlSessionFactory build(Reader reader)
    

    inputStream和Reader都表示xml配置文件地址,environment表示是环境,properties表示配置信息

    Configuration config 表示最终要构建的对象,xml方式,最终也是在构建Configuration对象,它表示全局配置

    XML配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC" />
                <!-- 配置数据库连接信息 -->
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.jdbc.Driver" />
                    <property name="url" value="jdbc:mysql://xx.xx.xx.xx:3306/table" />
                    <property name="username" value="root" />
                    <property name="password" value="123456" />
                </dataSource>
            </environment>
        </environments>
        <mappers>
            <mapper resource="TestMapper.xml"></mapper>
        </mappers>
    </configuration>
    
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    InputStream is = Thread.currentThread().getContextClassLoader()
            .getResourceAsStream("configuration.xml");
    SqlSessionFactory build = builder.build(is);  // 读取配置文件并且构建SqlSessionFactory
    System.out.println(build);
    

二、SqlSessionFactory获取

通过**SqlSessionFactory build = builder.build(is);**获取到SqlSessionFactory

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

SessionFactory是工厂,所有的session都是从该对象产生,全局单例。默认的实现对象为DefaultSqlSessionFactory

三、SqlSession获取

DefaultSqlSessionFactory中有多种获取SqlSession的方式,如下

public SqlSession openSession()

@Override
public SqlSession openSession(boolean autoCommit)

@Override
public SqlSession openSession(ExecutorType execType)

@Override
public SqlSession openSession(TransactionIsolationLevel level)
  ...

虽然有多种参数的方式,但是最终调用的都是下面这个方法openSessionFromDataSource

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();
  }
}

ExecutorType execType 执行器类型,枚举值

SIMPLE: 默认的执行器, 对每条sql进行预编译->设置参数->执行等操作,默认。

BATCH: 批量执行器, 对相同sql进行一次预编译, 然后设置参数, 最后统一执行操作

REUSE: REUSE 执行器会重用预处理语句(prepared statements)

TransactionIsolationLevel level 事务隔离级别

boolean autoCommit 是否自动提交

其中比较关键的三个步骤是用来获取***数据库连接和事务***的

final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);

很关键的一步,获取执行器

final Executor executor = configuration.newExecutor(tx, execType);
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;
}

最后组装获取DefaultSqlSession对象

四、获取接口mapper的对象

在进行第一步构建时候,通过配置文件,会将所有的mapper进行注册,Configuration > MapperRegistry > knownMappers

knownMappers这个map中存储的是MapperProxyFactory对象,该对象保存了mapper接口的基本信息

TestMapper mapper = sqlSession.getMapper(TestMapper.class);

获取mapper对象的过程,实际上就是到knownMappers中获取接口的基本信息MapperProxyFactory,通过该对象使用JDK的代理,创建代理对象

mapperProxyFactory.newInstance(sqlSession);
public T newInstance(SqlSession sqlSession) {
  final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
  return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
  return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); // JDK动态代理
}

五、执行方法

public static void main(String[] args) {
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    InputStream is = Thread.currentThread()
            .getContextClassLoader()
            .getResourceAsStream("configuration.xml");
    SqlSessionFactory build = builder.build(is);
    SqlSession sqlSession = build.openSession();
    TestMapper mapper = sqlSession.getMapper(TestMapper.class);

    Integer count = mapper.count();
    System.out.println(count);

}
<mapper namespace="com.mybatistest.mapper.TestMapper">

    <select id="count" resultType="int">
        select count(1) from person
    </select>

</mapper>

1、在调用接口方法时,实际上进入了SqlSession中,在SqlSession中对于数据库操作实际上只有两种,分别如下,虽然也区分了类似与selectOne,insert,delete等不同入参方法,但是最终都是调用的下面两个方法

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds)  // 查询操作
public int update(String statement, Object parameter)  // 增删改操作

statement 接口全路径加方法名

parameter 输入参数

rowBounds 分页对象

2、在selectList方法中通过statement在configuration中获取MappedStatement对象,该对象包含sql以及出参入参等所有信息

MappedStatement ms = configuration.getMappedStatement(statement);

3、通过SqlSession中的执行器,调用执行器中的query方法

executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);

4、query方法中,获取sql对象BoundSql以及session级别的缓存Key

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

5、调用抽象对象<基本执行器BaseExecutor> 的query方法,其中会通过Key进行获取是否存在缓存,如果不存在则进行查询

list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
  handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
  list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}

6、封装Statement对象(JDBC),获取**StatementHandler接口 对象,该对象封装了对JDBC的操作

ReuseExecutor.java

@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
  Configuration configuration = ms.getConfiguration();
  StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
  Statement stmt = prepareStatement(handler, ms.getStatementLog());
  return handler.query(stmt, resultHandler);
}

7、执行sql,并且通过ResultSetHandler对象封装返回值

PreparedStatementHandler.java

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  ps.execute();  
  return resultSetHandler.handleResultSets(ps);
}

看到这里应该太熟悉了吧,jdbc的方法

resultSetHandler.handleResultSets(ps) 对返回值的封装

六、拦截器插件

Executor在调用openSession()的时候会创建拦截器,在很多地方会创建,也在很多地方会执行,这里先举例一个执行的地方,就是在之前sql查询前,获取***StatementHandler***对象是进行执行。

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;
}
private final List<Interceptor> interceptors = new ArrayList<>();

public Object pluginAll(Object target) {
  for (Interceptor interceptor : interceptors) {
    target = interceptor.plugin(target);
  }
  return target;
}

进入代码,最关键的是代码是Plugin.wrap(target, this);

public static Object wrap(Object target, Interceptor interceptor) {
  Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
  Class<?> type = target.getClass();
  Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
  if (interfaces.length > 0) {
    return Proxy.newProxyInstance(
        type.getClassLoader(),
        interfaces,
        new Plugin(target, interceptor, signatureMap));
  }
  return target;
}

这里用到了责任链模式,依次执行每一个拦截器

七、总结

关键对象的作用
关键对象以及作用

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值