案例代码,
上一个博客已经分析了 SqlSessionFactory 的 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 的交互
看完这篇博客, 再加上自己的学习, 相信, 够面试的喝一壶了