下图中的3行代码基本就是mybatis的整个执行过程,下面我们来一步一步的看
1. 获取SqlSession
openSession方法在DefaultSqlSessionFactory中重载了多次,最终调用的是openSessionFromDataSource方法,构造一个DefaultSqlSession对象。
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 获取环境对象
final Environment environment = configuration.getEnvironment();
// 根据环境对象获取事务工厂,如果没有配置则使用 ManagedTransactionFactory
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 获取Transaction
// 强大的spring,利用SpringManagedTransactionFactory生成的是 SpringManagedTransaction
// 这里默认生成的是 ManagedTransaction
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 获取executor
final Executor executor = configuration.newExecutor(tx, execType);
// 设置DefaultSqlSession
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();
}
}
newExecutor方法比较有意思,其他的都是根据配置实例化对象。
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
// executorType 默认配置为 ExecutorType.SIMPLE
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
// 获取批处理的executor
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
}
// 获取可重用 Statement 的executor
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;
}
这里有一个mybatis plugin的使用
2.获取对应的mapper对象
接下来是获取对应的mapper类对象,也就是代理对象,继续看DefaultSqlSession的getMapper,直接调用的configuration.getMapper然后调用的是mapperRegistry.getMapper,所以我们直接看mapperRegistry.getMapper
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// 根据类对象获取对应的代理对象
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
// <1>生成代理类
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
这里生成一个MapperProxy对象,调用这个对象的newInstance方法时会为对应的接口生成一个jdk的动态代理类。所以接下来的执行过程也就是看MapperProxy类的invoke方法。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//如果是Object中定义的方法,直接执行。如toString(),hashCode()等
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
// 如果是default类型方法
// 利用 MethodHandles 执行 default方法
else if (method.isDefault()) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
// 缓存method,或生成一个新的MapperMethod对象。
final MapperMethod mapperMethod = cachedMapperMethod(method);
// 执行
return mapperMethod.execute(sqlSession, args);
}
先看一下MapperMethod这个类,然后再接着看执行过程。
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
SqlCommand和MethodSignature都是MapperMethod的内部类,SqlCommand有两个属性:name(类名.方法名),type(方法对应的xml标签类型,如:INSERT,UPDATE等)。MethodSignature保存的是方法对应的入参和出参类型。他们的构造方法如下:
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
final String methodName = method.getName();
final Class<?> declaringClass = method.getDeclaringClass();
// 根据类名.方法名获取对应的MappedStatement对象
// 或者是递归查找父类,根据父类名.方法名获取
MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass, configuration);
if (ms == null) {
if (method.getAnnotation(Flush.class) != null) {
name = null;
type = SqlCommandType.FLUSH;
} else {
throw new BindingException("Invalid bound statement (not found): "
+ mapperInterface.getName() + "." + methodName);
}
} else {
name = ms.getId();
// 方法对应的xml标签类型
type = ms.getSqlCommandType();
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
}
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);
this.returnsOptional = Optional.class.equals(this.returnType);
// 获取 @mapKey 注解配置
this.mapKey = getMapKey(method);
this.returnsMap = this.mapKey != null;
// 获取 RowBounds 在方法入参中的 index
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
// 获取 ResultHandler 在方法入参中的 index
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
// 解析 @Param 注解,
this.paramNameResolver = new ParamNameResolver(configuration, method);
}
接下来继续看mapperMethod的execute方法:
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
// 不同的 sql 类型调用不同的方法
switch (command.getType()) {
case INSERT: {
// 将方法的入参转换为具体 sql 的入参
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
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);
if (method.returnsOptional()
&& (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
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()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
里面涉及到了所有的sql执行预期,我们挑两个来分析,1.sqlSession.update。2.executeForMany
3.执行
3.1executeForMany(一次查询多个结果)
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
// 根据@Param或者是默认命名param1,param2。集合所有参数
Object param = method.convertArgsToSqlCommandParam(args);
// 内存分页
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
<1>
result = sqlSession.selectList(command.getName(), param, rowBounds);
}
// 不分页
else {
result = sqlSession.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;
}
接着看<1>处的代码
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// 获取对应的MappedStatement,这里会再次解析,初始化时解析失败的mapper接口方法和xml中的标签
MappedStatement ms = configuration.getMappedStatement(statement);
// <1> 调用具体的executor执行查询
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();
}
}
关于Executor接口,CachingExecutor当开启mybatis的二级缓存时使用,默认使用SimpleExecutor,因为一些方法在它的父类,BaseExecutor中,所以我们接下来进入BaseExecutor继续分析。
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// BoundSql用于存放sql和sql参数
BoundSql boundSql = ms.getBoundSql(parameter);
// 一级缓存key
// key的生成策略:id + offset + limit + sql + param value + environment id
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
继续看query方法
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 (closed) {
throw new ExecutorException("Executor was closed.");
}
// 清除一级缓存
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
// 增加嵌套查询次数
queryStack++;
// 缓存中获取
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) {
// 执行延迟加载
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
// 如果缓存级别是 LocalCacheScope.STATEMENT ,则进行清理
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
执行update,insert,delete,flushCache="true",commit,rollback,LocalCacheScope.STATEMENT等情况下,一级缓存就都会被清空,接下来继续看queryFromDatabase方法是如何查询数据库的
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 {
// 执行读操作
// <1>
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;
}
<1>处的方法由BaseExecutor的子类实现。SimpleExecutor每次开始读或写操作,都创建对应的 Statement 对象,执行完成后,关闭该 Statement 对象。BatchExecutor批处理相关。ReuseExecutor* 每次开始读或写操作,优先从缓存中获取对应的 Statement 对象,如果不存在,才进行创建,执行完成后,不关闭该 Statement 对象,其它的,和 SimpleExecutor 是一致的。
重点看看SimpleExecutor,因为它是默认实现方式,后面的是关于sql的执行以及结果的映射。继续看simpleExecutor对doQuery方法的实现
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();
// 默认使用PreparedStatementHandler
// <1>
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 初始化StatementHandler对象,设置sql的参数
// <2>
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
<1>处是创建新的StatementHandler,可以看到RoutingStatementHandler起始就是一个包装,正真工作的是delegate属性
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// 根据 StatementType 获取对应的 StatementHandler
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
// 插件
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
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());
}
}
继续看<2>的代码
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
// 获得 Connection 对象,如果是debug日志级别会多层 包装,创建代理类
// 主要用于打印各种日志
Connection connection = getConnection(statementLog);
// 创建 Statement 或 PrepareStatement 对象
// 并且如果需要返回主键,则使用 connection.prepareStatement 设置主键
stmt = handler.prepare(connection, transaction.getTimeout());
// 设置 SQL 上的参数,例如 PrepareStatement 对象上的占位符
handler.parameterize(stmt);
return stmt;
}
继续看handler.query方法
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
// 执行查询
ps.execute();
// 处理返回结果
return resultSetHandler.handleResultSets(ps);
}
接下来是 resultSetHandler.handleResultSets 结果映射相关,我们在后文再介绍。
3.2 sqlSession.update(执行查询)
执行完查询后会调用rowCountResult对结果进行一个简单的处理然后返回结果,接下来分析下update方法的执行过程。
public int update(String statement, Object parameter) {
try {
// 标识是否发生数据变更
dirty = true;
// 获取 MappedStatement
MappedStatement ms = configuration.getMappedStatement(statement);
// wrapCollection 用于包装入参
return executor.update(ms, wrapCollection(parameter));
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
private Object wrapCollection(final Object object) {
if (object instanceof Collection) {
StrictMap<Object> map = new StrictMap<>();
map.put("collection", object);
if (object instanceof List) {
map.put("list", object);
}
return map;
} else if (object != null && object.getClass().isArray()) {
StrictMap<Object> map = new StrictMap<>();
map.put("array", object);
return map;
}
return object;
}
public int update(MappedStatement ms, Object parameter) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
// 清除缓存
clearLocalCache();
return doUpdate(ms, parameter);
}
doUpdate方法由baseExecutor方法的子类实现,我们继续看simpleExecutor对它的实现:
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
} finally {
closeStatement(stmt);
}
}
public int update(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
// 返回影响的行数
int rows = ps.getUpdateCount();
// 获取入参
Object parameterObject = boundSql.getParameterObject();
// 设置主键值
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
return rows;
}
此处是执行keyGenerator的processAfter方法,processBefore方法在各个statementHandler构造方法中调用,也就是configuration中的newStatementHandler方法调用RoutingStatementHandler的构造方法时调用。