mybatis 创建session, 缓存, 执行SQL

案例代码,

上一个博客已经分析了 SqlSessionFactory 的 build , 本文内容部分知识基于上篇的文章

mybatis 的初始化, build

这节我们分析, 开启session, 发送查询

public static void main(String[] args) throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    try {
        AreaMapper mapper = sqlSession.getMapper(AreaMapper.class);
        List<Area> all = mapper.getAll();
        for (Area item : all)
            System.out.println(item.getAreaName());
    } finally {
        sqlSession.close();
    }
}

好的, 让我们开始吧

openSession()

SqlSession 是一个什么东西, 可以理解为对数据库一系列的操作, 发送增删改查操作

更可以获取一个mapper, 通过预定的规范, 调用方法即可发送sql, 执行数据库操作

now, 同志们是不是等不及, 那我们现在马上就来看看吧, = _ =, 探究其中的奥秘

在 build 的最终是创建了一个 DefaultSqlSessionFactory
所以 sqlSessionFactory.openSession(); 是调用了 DefaultSqlSessionFactory 中的方法

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

class DefaultSqlSessionFactory {
    @Override
    public SqlSession openSession() {
        // configuration.getDefaultExecutorType() 默认能是一个SIMPLE
        // 解析标签 <setting name="defaultExecutorType" value="SIMPLE"/> 得到的
        // 没有配置默认就是SIMPLE
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
    }
    
    private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        Transaction tx = null;
        try {
            // 环境参数, 包含事务工厂, 数据源
            final Environment environment = configuration.getEnvironment();
            // 获取其中的 JdbcTransactionFactory, 为什么是jdbc呢? 请看上一篇文章
            final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
            
            // 1, 创建一个新的事务, 这个里面是很简单的对象创建, 就不就跟进去了
            // 意思就是创建了一个不是不是自动提交的事务, 里面包含了事务等级 和 数据源
            tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
            
            // 2, 创建一个执行器, 我们关注这个
            final Executor executor = configuration.newExecutor(tx, execType);
            
            // 3, 创建 DefaultSqlSession, 简单赋值
            return new DefaultSqlSession(configuration, executor, autoCommit);
        } catch (Exception e) {
            closeTransaction(tx);
            throw ExceptionFactory.wrapException("xxx" + e, e);
        } finally {
            ErrorContext.instance().reset();
        }
    }
}

class Configuration {
    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);
        }
        
        // 增强插件, 比如mybatis-plus
        executor = (Executor) interceptorChain.pluginAll(executor);
        return executor;
    }  
}
CachingExecutor

这里便能看到所谓的缓存执行器

public class CachingExecutor implements Executor {
    private Executor delegate;
    private TransactionalCacheManager tcm = new TransactionalCacheManager();

    // 构造器还是很简单的, 就是把实际的执行器给封装了
    public CachingExecutor(Executor delegate) {
        this.delegate = delegate;
        delegate.setExecutorWrapper(this);
    }
    
    // 稍微看看这个方法, 其他的感兴趣自己去看
    @Override
    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
        throws SQLException {
        // 查询缓存
        Cache cache = ms.getCache();
        if (cache != null) {
            flushCacheIfRequired(ms);
            if (ms.isUseCache() && resultHandler == null) {
                ensureNoOutParams(ms, parameterObject, boundSql);
                
                // 获取缓存数据
                @SuppressWarnings("unchecked")
                List<E> list = (List<E>) tcm.getObject(cache, key);
                if (list == null) {
                    list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                    tcm.putObject(cache, key, list); // issue #578 and #116
                }
                // 返回缓存数据
                return list;
            }
        }
        return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    }

}

小结

1, 我们发现了mybatis的缓存执行器, 默认是开启的

2, mysql 连接并不是在 openSession 打开的

3, **sqlSession **包含 **CachingExecutor **包含 **SimpleExecutor **而 SimpleExecutor 里面执行的时候才会开启连接

获取 mapper 的代理对象

从这里开始, 分析 调用一个接口 mapper , 怎么就把sql 发送出去, mapper 肯定是被代理了

下面就将解密

try {
    // 分析的是这行代码
    AreaMapper mapper = sqlSession.getMapper(AreaMapper.class);
    List<Area> all = mapper.getAll();
    for (Area item : all)
        System.out.println(item.getAreaName());
} finally {
    sqlSession.close();
}

openSession 的时候返回了一个 DefaultSqlSession.class 对象

class DefaultSqlSession {
    @Override
    public <T> T getMapper(Class<T> type) {
        // 1 
        return configuration.<T>getMapper(type, this);
    } 
}

class Configuration {
    protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
    
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        // 2 从mapper 注册表里面获取一个 mapper 的代理对象
        return mapperRegistry.getMapper(type, sqlSession);
    }
}

class MapperRegistry {
    
    public MapperProxyFactory(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }
    
    // 3, 我们先关注添加方法, 详情请查看上一个博客
    public <T> void addMapper(Class<T> type) {
        // ...代码省略, 可以看看 MapperProxyFactory 的构造函数
        knownMappers.put(type, new MapperProxyFactory<T>(type));
    }
    
    // 4, 通过 代理工厂 获取 mapper 代理对象
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
        try {
            // 从这里接着分析
            return mapperProxyFactory.newInstance(sqlSession);
        } catch (Exception e) {
            throw new BindingException("Error getting mapper instance. Cause: " + e, e);
        }
    }
}

// 每一个mapper 文件都有一个代理工厂
class MapperProxyFactory {
    
    // 被代理的class
    private final Class<T> mapperInterface;

    // 6, 返回了一个 mapper 代理对象
    protected T newInstance(MapperProxy<T> mapperProxy) {
        // 返回代理对象
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
    }

    // 5, 到这里创建代理对象
    public T newInstance(SqlSession sqlSession) {
        final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
        return newInstance(mapperProxy);
    }
}

小结:

1, getMapper 是从 configuration 的 mapper 管理器从获取一个 MapperProxyFactory

每一个 mapper 接口, 都会生成一个 MapperProxyFactory

2, 通过代理工厂, 代理工厂里面保存了 mapper 的 全路径类名

3, 通过 MapperProxy 代理 mapper

执行 mapper 的方法

我们通过上面就可以知道 getMapper 返回的是 MapperProxy , 也就是说在 MapperProxy 当中, 肯定有 invoke 方法

try {
    AreaMapper mapper = sqlSession.getMapper(AreaMapper.class);
    // 现在分析的是这个
    List<Area> all = mapper.getAll();
    for (Area item : all)
        System.out.println(item.getAreaName());
} finally {
    sqlSession.close();
}

MapperProxy.java

class MapperProxy {
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            if (Object.class.equals(method.getDeclaringClass())) {
                // 这个是直接执行了
                return method.invoke(this, args);
            } else if (isDefaultMethod(method)) {
                return invokeDefaultMethod(proxy, method, args);
            }
        } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
        }
        // 1, 我们关注这个
        final MapperMethod mapperMethod = cachedMapperMethod(method);
        // 方法执行
        return mapperMethod.execute(sqlSession, args);
    }
    
    private MapperMethod cachedMapperMethod(Method method) {
        MapperMethod mapperMethod = methodCache.get(method);
        // 刚开始这里是null
        if (mapperMethod == null) {
            // 2, 创建一个 MapperMethod
            mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
            methodCache.put(method, mapperMethod);
        }
        return mapperMethod;
    }
}
MapperMethod.java
class MapperMethod {
    private final SqlCommand command;
    private final MethodSignature method;

    public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
        // 获取声明
        this.command = new SqlCommand(config, mapperInterface, method);
        // 方法签名, 就是获取mapper 方法的一些基本信息
        this.method = new MethodSignature(config, mapperInterface, method);
    }
}
**SqlCommand.java **

主要的目的就是拿到 mapper 声明, 至于声明是怎么解析来的, 请按照这个路线去分析,

// 拿到声明ID, 在解析config.xml 的时候创建的声明, configuration
// 可以去详细的看看 
XMLConfigBuilder.parseConfiguration()
-> mapperElement(root.evalNode("mappers"));
	-> configuration.addMapper(mapperInterface);
		->	org.apache.ibatis.binding.MapperRegistry#addMapper
			->	parser.parse();
				->	MapperAnnotationBuilder.loadXmlResource()
					-> XMLMapperBuilder.parse()
						->	XMLMapperBuilder.parsePendingStatements()
						
end: MapperBuilderAssistant.addMappedStatement(args)
class SqlCommand {
    private final String name;
    private final SqlCommandType type;

    // 有省略代码的哦
    public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
        String statementName = mapperInterface.getName() + "." + method.getName();
       	// 这里就会去 configuration 当中拿声明
        ms = configuration.getMappedStatement(statementName);
        name = ms.getId();
        type = ms.getSqlCommandType();
        if (type == SqlCommandType.UNKNOWN) {
            throw new BindingException("Unknown execution method for: " + name);
        }
    }
}
MethodSignature.java
class MethodSignature {
    public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
        // 获取返回值类型
        Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
        if (resolvedReturnType instanceof Class<?>) {
            this.returnType = (Class<?>) resolvedReturnType;
        } else if (resolvedReturnType instanceof ParameterizedType) {
            this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
        } else {
            this.returnType = method.getReturnType();
        }
        
        // 是否模样返回值
        this.returnsVoid = void.class.equals(this.returnType);
        // 是否返回多个结果
        this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray());
        // 返回游标
        this.returnsCursor = Cursor.class.equals(this.returnType);
        // @MapKey 注解
        this.mapKey = getMapKey(method);
        // 是否返回一个map
        this.returnsMap = (this.mapKey != null);
        // 参数长度
        this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
        this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
        this.paramNameResolver = new ParamNameResolver(configuration, method);
    }
}

上面的分析, 为我们创建了一个 MapperMethod 对象

包含了 mapper 声明, 还有执行接口的信息, 现在我们就要执行了

mapperMethod.execute(sqlSession, args)
class MapperProxy {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            if (Object.class.equals(method.getDeclaringClass())) {
                // 这个是直接执行了
                return method.invoke(this, args);
            } else if (isDefaultMethod(method)) {
                return invokeDefaultMethod(proxy, method, args);
            }
        } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
        }
        // 1, 这个我们已经分析了
        final MapperMethod mapperMethod = cachedMapperMethod(method);
        // 2, 现在分析这个
        return mapperMethod.execute(sqlSession, args);
    }
}

MapperMethod.java

class MapperMethod {
    
    public Object execute(SqlSession sqlSession, Object[] args) {
        Object result;
        // 我们的是SELECT
        switch (command.getType()) {
            // ... 前面的是增改删
            case SELECT:
                if (method.returnsVoid() && method.hasResultHandler()) {
                    executeWithResultHandler(sqlSession, args);
                    result = null;
                } else if (method.returnsMany()) {
                    // 1, 我们返回是一个集合, 既多个结果
                    result = executeForMany(sqlSession, args);
                } else if (method.returnsMap()) {
                    result = executeForMap(sqlSession, args);
                } else if (method.returnsCursor()) {
                    result = executeForCursor(sqlSession, args);
                } else {
                    Object param = method.convertArgsToSqlCommandParam(args);
                    result = sqlSession.selectOne(command.getName(), param);
                }
                break;
            case FLUSH:
                result = sqlSession.flushStatements();
                break;
            default:
                throw new BindingException("Unknown execution method for: " + command.getName());
        }
        if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
            // 如果有返回值, 但是方法是个viod , 抛异常
        }
        return result;
    }   
    
    private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
        List<E> result;
        Object param = method.convertArgsToSqlCommandParam(args);
        // 是否有参数啊
        if (method.hasRowBounds()) {
            RowBounds rowBounds = method.extractRowBounds(args);
            result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
        } else {
            // 2, 没得, 我们走这里
            result = sqlSession.<E>selectList(command.getName(), param);
        }
        // issue #510 Collections & arrays support
        if (!method.getReturnType().isAssignableFrom(result.getClass())) {
            if (method.getReturnType().isArray()) {
                return convertToArray(result);
            } else {
                return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
            }
        }
        return result;
    }
}

DefaultSqlSession.java

在开启 openSession 的时候, 小结就提到, 这个东西最终还是会到一个叫SimpleExecutor 中去, 虽然封装的很复杂,

我们要 拨开云雾见青天

class DefaultSqlSession {
    @Override
    public <E> List<E> selectList(String statement, Object parameter) {
        // 1,
        // RowBounds.DEFAULT 默认分页
        return this.selectList(statement, parameter, RowBounds.DEFAULT);
    }
    
    @Override
    public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        try {
            // 2, 还是从 configuration 对象取 MappedStatement 
            MappedStatement ms = configuration.getMappedStatement(statement);
            // 3, 交由执行器执行sql, 下面即将进入的二级缓存执行器, 对 SimpleExecutor 增强
            return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
        } catch (Exception e) {
            throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
        } finally {
            ErrorContext.instance().reset();
        }
    }
}
二级缓存 CachingExecutor.java
class CachingExecutor {
    @Override
    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        // 获取声明中的sql 信息, 参数啦, 返回类型啦
        BoundSql boundSql = ms.getBoundSql(parameterObject);
        // 根据综合信息创建一个缓存key
        CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
        // 调用缓存查询
        return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    }
    
    @Override
    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
        throws SQLException {
        Cache cache = ms.getCache();
        // 判断有没有开启缓存, 怎么开启缓存
        if (cache != null) {
            flushCacheIfRequired(ms);
            if (ms.isUseCache() && resultHandler == null) {
                ensureNoOutParams(ms, parameterObject, boundSql);
                @SuppressWarnings("unchecked")
                List<E> list = (List<E>) tcm.getObject(cache, key);
                if (list == null) {
                    list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                    tcm.putObject(cache, key, list); // issue #578 and #116
                }
                return list;
            }
        }
        return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    }
}

这里说一说二级缓存怎么开启,

 <settings>
     <setting name="defaultExecutorType" value="SIMPLE"/>
     <setting name="cacheEnabled" value="true"/>
</settings>

@CacheNamespace
public interface AreaMapper {

开启这个配置也没有用, 通过源码发现, loadXmlResource() 的时候, mapper 声明就已经产生了, 而此刻的cache 还是一个 null, parseCache() 把 cache 重新赋值, 也不会更新到 mapper 声明当中, 所以, 博主也没有把二级缓存开启来, 知道的原因小伙伴可以留言,

class MapperAnnotationBuilder {
    public void parse() {
        String resource = type.toString();
        if (!configuration.isResourceLoaded(resource)) {
            // 1
            loadXmlResource();
            configuration.addLoadedResource(resource);
            assistant.setCurrentNamespace(type.getName());
            // 2
            parseCache();
            parseCacheRef();
            Method[] methods = type.getMethods();
            for (Method method : methods) {
                try {
                    // issue #237
                    if (!method.isBridge()) {
                        parseStatement(method);
                    }
                } catch (IncompleteElementException e) {
                    configuration.addIncompleteMethod(new MethodResolver(this, method));
                }
            }
        }
        parsePendingMethods();
    } 
}

好, 我们接着分析 mapper 执行

class CachingExecutor {
   
    @Override
    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
        throws SQLException {
        Cache cache = ms.getCache();
        // 判断有没有开启缓存, 怎么开启缓存
        if (cache != null) {
           // 我们并没有开启二级缓存
        }
        // 1, 下面进入到真的执行器, 我们这里是一个SimpleExecutor 
        return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    }
}

//  SimpleExecutor extends BaseExecutor
class BaseExecutor {
    
    // 2
    @Override
    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());
        
        // 查询是零了, 这是整个session范围, 并且当前声明是一个清楚操作, CUD 都是
        if (queryStack == 0 && ms.isFlushCacheRequired()) {
            clearLocalCache();
        }
        List<E> list;
        try {
            queryStack++;
            // localCache 一级缓存, 默认开启
            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);
            }
        } finally {
            queryStack--;
        }
        if (queryStack == 0) {
            // 代码省略...
        }
        return list;
    }
    
    // 3, 封装一下缓存, 调用模板方法, 由子类 SimpleExecutor 去执行真正的查询
    private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        List<E> list;
        // 预置缓存
        localCache.putObject(key, EXECUTION_PLACEHOLDER);
        try {
            // 设计模式, 调用子类实现
            list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
        } finally {
            localCache.removeObject(key);
        }
        // 保存缓存结果
        localCache.putObject(key, list);
        if (ms.getStatementType() == StatementType.CALLABLE) {
            localOutputParameterCache.putObject(key, parameter);
        }
        return list;
    }

}

我们能发现一个问题 , 一级缓存的清除操作的计数器 queryStack 并不是线程安全, 所以呀, session 最好不是线程共享的, 不然会出现, 某一个 线程拿到旧的结果 , 脏数据

SimpleExecutor.java

好, 我们接着往下看, SimpleExecutor 怎么执行具体的查询

class SimpleExecutor {
    
    @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();
            
            // 1, 这个里面都是一些赋值, 感兴趣的可以进去看看, 封装参数处理, 返回值处理
            // 返回是的一个 领路者操作者 RoutingStatementHandler.java
            StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
            
            // 2, 有意思的在这里, 预处理SQL,  JDBC 玩过吧, 预处理都知道吧, 都不难, 
            // 我们不能被事务的表面现象所迷惑, 不想分析的直接看 下一个标题 instantiateStatement
            stmt = prepareStatement(handler, ms.getStatementLog());
            
            // 3, 执行 预处理
            return handler.<E>query(stmt, resultHandler);
        } finally {
            closeStatement(stmt);
        }
    }
    
    // 4
    private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        Statement stmt;
        // 调用 jdbc 事务管理器, 再调用数据源, 获取连接
        Connection connection = getConnection(statementLog);
        // 开始预处理SQL
        stmt = handler.prepare(connection, transaction.getTimeout());
        handler.parameterize(stmt);
        return stmt;
    }
    
    
}

// 5, 引路者开始带我们了, 中介者模式
class RoutingStatementHandler {
    
    // 引路者的路子, 遮眼法, 如果DEBUG, 可以不去关注
    public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        switch (ms.getStatementType()) {
            case STATEMENT:
                delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
                break;
            case PREPARED:
                // 它, 我们的预处理执行者, 我们的实际合作者
                delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
                break;
            case CALLABLE:
                delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
                break;
            default:
                throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
        }

    }
    
    @Override
    public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
        // 6, 找到实际的合作人, 
        return delegate.prepare(connection, transactionTimeout);
    }
}

// PreparedStatementHandler extends BaseStatementHandler
class BaseStatementHandler {
    
    @Override
    public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
        ErrorContext.instance().sql(boundSql.getSql());
        Statement statement = null;
        try {
            // 其他的代码都是遮眼法, 开始初始化声明, 模板方法, 回到子类 PreparedStatementHandler
            statement = instantiateStatement(connection);
            setStatementTimeout(statement, transactionTimeout);
            setFetchSize(statement);
            return statement;
        } catch (SQLException e) {
            closeStatement(statement);
            throw e;
        } catch (Exception e) {
            closeStatement(statement);
            throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
        }
    }
}
instantiateStatement

就是 jdbc 操作咯, 我们不能再继续往下了, 再往下就不是研究mybatis了

@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
    String sql = boundSql.getSql();
    if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
        String[] keyColumnNames = mappedStatement.getKeyColumns();
        if (keyColumnNames == null) {
            // 预处理SQL
            return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
        } else {
            return connection.prepareStatement(sql, keyColumnNames);
        }
    } else if (mappedStatement.getResultSetType() != null) {
        return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    } else {
        return connection.prepareStatement(sql);
    }
}

小结:

1, 每次真实查询都会开启一次数据库连接, 再没有配置连接池的情况下

2, 再怎么困难, 最终还是对jdbc的封装

下面就是执行预处理了

class SimpleExecutor {
    
    @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.<E>query(stmt, resultHandler);
        } finally {
            closeStatement(stmt);
        }
    }
}

// 领路者
class RoutingStatementHandler {
    @Override
    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        return delegate.<E>query(statement, resultHandler);
    }
}

// 实际执行者
class PreparedStatementHandler {
    
    @Override
    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        // 执行发送SQL
        ps.execute();
        // 封装返回数据
        return resultSetHandler.<E> handleResultSets(ps);
    }
}

好, 我们的 mybatis 分析之路, 到此结束, 学东西总得有疑问,

才能在学习中发现自己没有发现的东西, 才能得到学习的乐趣

面试的时候

1, session 线程共享可能出现的问题

2, 一级缓存 和 二级缓存

3, 每次执行SQL, 都会创建一个连接

4, 用上连接池以后又是怎样

5, 与spring 的交互

6, 与mybatis-plus 的交互

看完这篇博客, 再加上自己的学习, 相信, 够面试的喝一壶了

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值