看源码都要带着问题去看,比如
- UserMapper.java只有接口而没有实现类,那么是如何执行的呢?
- mybatis中一级缓存是如何进行缓存与维护的?
- 底层是如何执行query查询的
- 查询后的结果是如何处理的,为什么可以将结果集自动映射到对象中去?
让我们带着上面的问题来到mybatis框架底层一探究竟吧
四大组件
开始之前我们先了解下几个重要的组件它们的作用
我们使用的时候,都是从SqlSession中的对象开始,它是属于接口层
Mapper执行的过程是通过Executor、StatementHandler、ParameterHandler和ResultHandler来完成数据库操作和结果返回的:
-
Executor
代表执行器,由它来调度StatementHandler、ParameterHandler、ResultHandler等来执行对应的SQL。
-
StatementHandler
作用是使用数据库的Statement(PreparedStatement)执行操作,起到承上启下的作用。
-
ParameterHandler
用于SQL对参数的处理。
-
ResultHandler
进行最后数据集(ResultSet)的封装返回处理的。
查询User记录的简单例子
public static void main(String[] args) {
SqlSession sqlSession = null;
try {
// 获取到sqlSessiion
sqlSession = SqlSessionFactoryUtil.openSqlSession();
// 获取到Mapper
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 查询记录
User user = userMapper.getUserById(2L);
System.out.println("姓名:" + user.getName() + ",年龄:" + user.getAge());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (sqlSession != null) {
sqlSession.close();
}
}
}
}
从查询一条用户记录开始为起点,进行源码分析
六大阶段
- 阶段1:获得Mapper动态代理阶段
- 阶段2:获得MapperMethod对象
- 阶段3:根据SQL指令跳转执行语句
- 阶段4:查询前的缓存处理
- 阶段5:执行DB查询操作
- 阶段6:针对ResultSet结果集转换为POJO
第一阶段:获取Mapper动态代理阶段
我们自定义的Mapper接口想要发挥功能,必须有具体的实现类,在MyBatis中是通过为Mapper每个接口提供一个动态代理类来实现的。整个过程主要有四个类:MapperRegistry、MapperProxyFactory、MapperProxy、MapperMethod。
- MapperRegistry
是Mapper接口及其对应的代理对象工厂的注册中心。 - MapperProxyFactory
是MapperProxy的工厂类,主要方法就是包装了Java动态代理的Proxy.newProxyInstance()方法。 - MapperProxy
是一个动态代理类,它实现了InvocationHandler接口。对于代理对象的调用都会被代理到InvocationHandler#invoke方法上。 - MapperMethod
包含了具体增删改查方法的实现逻辑。
第一阶段关键代码:
sqlSession.getMapper(UserMapper.class);
调用的是DefaultSqlSession中的getMapper()方法
public class DefaultSqlSession implements SqlSession {
...
// eg1: type=UserMapper.class
@Override
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}
...
}
然后进入Configuration,这个类是mybatis配置信息类,将mybatis的xml配置文件和映射mapper的xml配置文件,读入到该类中进行使用
public class Configuration {
// eg1: type=UserMapper.class sqlSession=DefaultSqlSession实例
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
}
MapperRegistry是Mapper接口及其对应的代理对象工厂的注册中心
public class MapperRegistry {
// eg1: type=UserMapper.class
@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
/**
* 加载mybatis-config.xml配置的<mapper>配置,根据指定type,查找对应的MapperProxyFactory对象
**/
// eg1: 获得UserMapper的mapperProxyFactory
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
/**
* 如果没配置<mapper>,则找不到对应的MapperProxyFactory,抛出BindingException异常
*/
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
/** 使用该工厂类生成MapperProxy的代理对象*/
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
}
关键方法在:mapperProxyFactory.newInstance(sqlSession);
public class MapperProxyFactory<T> {
/**
* 通过动态代理,创建mapperInterface的代理类对象
*/
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] {mapperInterface}, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
/**
* 创建MapperProxy对象,每次调用都会创建新的MapperProxy对象,MapperProxy implements InvocationHandler
*/
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
接下来就是一直返回到最初的调用为UserMapper对象
总的来说就是通过预先扫描的mapper接口,然后对此接口进行JDK动态代理,然后通过代理实现sql的执行(就是invoke())
第二阶段:获取MapperMethod对象
执行如下代码的源码过程分析:
User user = userMapper.getUserById(2L);
/**
* 对代理类的所有方法的执行,都会进入到invoke方法中
*/
// eg1: UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// User user = userMapper.getUserById(2L); args = {2L}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
/** 如果被代理的方法是Object类的方法,如toString()、clone(),则不进行代理 */
// eg1: method.getDeclaringClass()==interface mapper.UserMapper 由于被代理的方法是UserMapper的getUserById方法,而不是Object的方法,所以返回false
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
/** 如果是接口中的default方法,则调用default方法 */
else if (isDefaultMethod(method)) { // eg1: 不是default方法,返回false
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
// eg1: method = public abstract vo.User mapper.UserMapper.getUserById(java.lang.Long)
/** 初始化一个MapperMethod并放入缓存中 或者 从缓存中取出之前的MapperMethod */
final MapperMethod mapperMethod = cachedMapperMethod(method);
// eg1: sqlSession = DefaultSqlSession@1953 args = {2L}
/** 调用MapperMethod.execute()方法执行SQL语句 */
return mapperMethod.execute(sqlSession, args);
}
然后我们来看下如何获取MapperMethod对象:
private MapperMethod cachedMapperMethod(Method method) {
/**
* 在缓存中查找MapperMethod,若没有,则创建MapperMethod对象,并添加到methodCache集合中缓存
*/
// eg1: 因为methodCache为空,所以mapperMethod等于null
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
// eg1: 构建mapperMethod对象,并维护到缓存methodCache中
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
// eg1: method = public abstract vo.User mapper.UserMapper.getUserById(java.lang.Long)
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
主要方法在new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
,进入这个类看看构造函数:
// 记录了SQL语句的名称和类型
private final SqlCommand command;
// Mapper接口中对应方法的相关信息
private final MethodSignature method;
// eg1: mapperInterface = interface mapper.UserMapper
// method = public abstract vo.User mapper.UserMapper.getUserById(java.lang.Long)
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
SqlCommand:主要维护两个属性
/** MappedStatement的唯一标识id */
private final String name;
/** sql的命令类型 UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH; */
private final SqlCommandType type;
MethodSignature:主要用于维护方法签名,比如方法的返回值类型、入参名称等等。具体参数如下:
private final boolean returnsMany; // 判断返回类型是集合或者数组吗
private final boolean returnsMap; // 判断返回类型是Map类型吗
private final boolean returnsVoid; // 判断返回类型是集void吗
private final boolean returnsCursor; // 判断返回类型是Cursor类型吗
private final Class<?> returnType; // 方法返回类型
private final String mapKey; // 获得@MapKey注解里面的value值
private final Integer resultHandlerIndex; // 入参为ResultHandler类型的下标号
private final Integer rowBoundsIndex; // 入参为RowBounds类型的下标号
private final ParamNameResolver paramNameResolver; // 入参名称解析器
至此,就获取到了一个MapperMethod对象。
大致流程如下:
- 如果不是Object默认方法或default类型的方法,则会从cachedMapperMethod中以method为key获取MapperMethod对象
- 如果没有在cache中,会new MapperMethod对象并维护到cache中
- new MapperMethod对象里,会维护SqlCommand和MethodSignate对象,其中
-
SqlCommand用于保存MapperStatement的唯一标识id和sql的命令类型(
MapperStatement:用于保存映射器的一个节点(select|insert|delete|update)。包括许多我们配置的SQL、SQL的id、缓存信息、resultMap、parameterType、resultType、languageDriver等重要配置内容。
-
MethodSignature主要维护方法签名,保存方法入参、返回值类型等等
-
第三阶段:根据SQL指令跳转执行语句
这个阶段主要将在代理的方法invoke中的最后一句代码:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
...
/** 调用MapperMethod.execute()方法执行SQL语句 */
return mapperMethod.execute(sqlSession, args);
}
首先我们进入到excute()方法看看:
/**
* MapperMethod采用命令模式运行,根据上下文跳转,它可能跳转到许多方法中。实际上它最后就是通过SqlSession对象去运行对象的SQL。
*/
// eg1: sqlSession = DefaultSqlSession@1953 args = {2L}
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
// eg1: command.getType() = SELECT
switch (command.getType()) {
case INSERT: {
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:
// eg1: method.returnsVoid() = false method.hasResultHandler() = false
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) { // eg1: method.returnsMany() = false
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) { // eg1: method.returnsMap() = false
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) { // eg1: method.returnsCursor() = false
result = executeForCursor(sqlSession, args);
} else {
// eg1: args = {2L}
/** 将参数转换为sql语句需要的入参 */
Object param = method.convertArgsToSqlCommandParam(args);
// eg1: sqlSession=DefaultSqlSession command.getName()="mapper.UserMapper.getUserById" param={"id":2L, "param1":2L}
/** 执行sql查询操作 */
result = sqlSession.selectOne(command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
...
return result;
}
根据不同的sql查询命令,进入不同的方法。
- 例子中,我们执行的是SELECT,所以会进入SELECT分支,返回的结果也是根据第二阶段获取的MethodSignature进行判断,是返回多条记录、单条记录还是map、Cursor类型,这里是单条记录
- 根据属性名与参值的映射关系,调用methodSignature实例的getNamedParams(args)方法进行对应,比如:
param={"id": 2L, "param1", 2L}
- 执行sql查询操作:
result = sqlSession.selectOne(command.getName(), param);
在第三阶段中
- 通过对mapperMethod中的methodSignature实例进行选择对应执行的方法
- 在select分支中对参数进行转换对应,准备开始执行selectOne()操作
第四阶段:查询前的缓存处理
这个阶段主要是对上个阶段的如下代码进行解析:
/** 执行sql查询操作 */
result = sqlSession.selectOne(command.getName(), param);
进入selectOne()方法实际调用的是DefaultSqlSession的方法:
// eg1: statement="mapper.UserMapper.getUserById" parameter={"id":2L, "param1":2L}
@Override
public <T> T selectOne(String statement, Object parameter) {
List<T> list = this.selectList(statement, parameter);
...
}
// 最终调用的是以下方法
// eg1: statement="mapper.UserMapper.getUserById" parameter={"id":2L, "param1":2L}
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// eg1: statement="mapper.UserMapper.getUserById"
MappedStatement ms = configuration.getMappedStatement(statement);
// eg1: executor=CachingExecutor
// wrapCollection(parameter)=parameter={"id": 2L, "param1", 2L}
// rowBounds=RowBounds.DEFAULT=new RowBounds()
// Executor.NO_RESULT_HANDLER=null
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();
}
}
- 需要根据之前sqlCommand中的id(id=“mapper.UserMapper.getUserById”)获取MappedStatement
- 然后调用CachingExcutor(带有sqlSession级别的缓存)的query方法
// eg1: parameterObject={"id":2L, "param1":2L} // rowBounds=new RowBounds() // resultHandler=null @Override public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { // eg1: parameterObject = {"id":2L, "param1":2L} /** 获得boundSql对象,承载着sql和对应的参数*/ BoundSql boundSql = ms.getBoundSql(parameterObject); // eg1: parameterObject = {"id":2L, "param1":2L} rowBounds = new RowBounds() /** 生成缓存key */ CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql); // eg1: parameterObject = {"id":2L, "param1":2L} rowBounds = new RowBounds() resultHandler = null /** 执行查询语句 */ return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }
- 获取BoundSql,承载着sql和对应的参数
- 生成一、二级缓存需要的缓存key
- 开始执行查询操作:
query(ms, parameterObject, rowBounds, resultHandler, key, boundSql)
执行查询操作:query(ms, parameterObject, rowBounds, resultHandler, key, boundSql)
// eg1: parameterObject = {"id": 2L, "param1", 2L} rowBounds = new RowBounds() resultHandler = null
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds,
ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
Cache cache = ms.getCache();
// eg1: cache = null
/** 如果在UserMapper.xml配置了<cache/>开启了二级缓存,则cache不为null*/
if (cache != null) {
/**
* 如果flushCacheRequired=true并且缓存中有数据,则先清空缓存
*
* <select id="save" parameterType="XXXXXEO" statementType="CALLABLE" flushCache="true" useCache="false">
* ……
* </select>
* */
flushCacheIfRequired(ms);
/** 如果useCache=true并且resultHandler=null*/
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);
/** 以cacheKey为主键,将结果维护到缓存中 */
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
// eg1: delegate = SimpleExecutor(BaseExecutor) parameterObject = {"id": 2L, "param1", 2L} rowBounds = new RowBounds() resultHandler = null
return delegate.<E>query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
真正调用的是Executor的实现类BaseExecutor实例的query()方法
// eg1: parameter = {"id": 2L, "param1", 2L} rowBounds = new RowBounds() resultHandler = null
@SuppressWarnings("unchecked")
@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());
// eg1: closed = false
if (closed) {
throw new ExecutorException("Executor was closed.");
}
// eg1: queryStack = 0 ms.isFlushCacheRequired() = false
/** 如果配置了flushCacheRequired=true并且queryStack=0(没有正在执行的查询操作),则会执行清空缓存操作*/
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
/** 记录正在执行查询操作的任务数*/
queryStack++; // eg1: queryStack=1
// eg1: resultHandler=null localCache.getObject(key)=null
/** localCache维护一级缓存,试图从一级缓存中获取结果数据,如果有数据,则返回结果;如果没有数据,再执行queryFromDatabase */
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
// eg1: list = null
if (list != null) {
/** 如果是执行存储过程 */
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
// eg1: parameter = {"id": 2L, "param1", 2L} rowBounds = new RowBounds() resultHandler = null
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
/** 延迟加载处理 */
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
// eg1: configuration.getLocalCacheScope()=SESSION
/** 如果设置了<setting name="localCacheScope" value="STATEMENT"/>,则会每次执行完清空缓存。即:使得一级缓存失效 */
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
- 如果开启了缓存,则尝试从缓存中获取结果
- 如果缓存中没有结果,则执行查询操作
- 如果没有开启缓存,则直接执行查询操作,真正查询操作调用的是
queryFromDatabase()
最后将查询的list结果返回,如果是selectOne(),只会list.get(0)
在第三阶段中,主要做了如下事情:
- 调用DefaultSession实例的selectOne(command.getName(), param);,获取在Configuration中对应的MappedStatement
- 在selectOne()中,会调用query()方法
- 获得BoundSql(承载sql和参数的对象)
- 生成一、二级缓存需要的缓存key
- 执行查询操作(会尝试从缓存中获取)
- 返回list.get(0)结果
第五阶段:执行DB查询操作(真正开始查询了)
在上一个阶段中,调用了执行sql的方法:
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
// eg1: parameter = {"id": 2L, "param1", 2L} rowBounds = new RowBounds() resultHandler = null
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds,
ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
// eg1: key = -445449180:-48278933:mapper.UserMapper.getUserById:0:2147483647:select id, name, age from tb_user where id = ?:2:dev
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
// eg1: SimpleExecutor.doQuery parameter = {"id": 2L, "param1", 2L} rowBounds = new RowBounds() resultHandler = null
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
/** 将查询结果放到一级缓存中,如果同一session中有相同查询操作,则可以直接从缓存中获取结果*/
localCache.putObject(key, list);
// eg1: ms.getStatementType() = PREPARED
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
主要执行语句是list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
,而真正调用的是SimpleExecutor类的doQuery()
// eg1: parameter = {"id": 2L, "param1", 2L} rowBounds = new RowBounds() resultHandler = null
@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();
/** 根据Configuration来构建StatementHandler */
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds,
resultHandler, boundSql);
// eg1: handler=RoutingStatementHandler
/** 然后使用prepareStatement方法,对SQL进行预编译并设置入参 */
stmt = prepareStatement(handler, ms.getStatementLog());
// eg1: handler=RoutingStatementHandler parameter = {"id": 2L, "param1", 2L} rowBounds = new RowBounds() resultHandler = null
/** 开始执行真正的查询操作。将包装好的Statement通过StatementHandler来执行,并把结果传递给resultHandler */
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
这里有三步:
-
根据Configuration来构建StatementHandler,并添加到拦截器链interceptorChain中
-
然后使用prepareStatement方法,对SQL进行预编译并设置入参
/** * 使用prepareStatement方法,对SQL编译并设置入参 * * @param handler * @param statementLog * @return * @throws SQLException */ // eg1: handler=RoutingStatementHandler private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; /** 获得Connection实例 */ Connection connection = getConnection(statementLog); // eg1: handler=RoutingStatementHandler /** 第一步:调用了StatementHandler的prepared进行了【sql的预编译】 */ stmt = handler.prepare(connection, transaction.getTimeout()); /** 第二步:通过PreparedStatementHandler的parameterize来给【sql设置入参】 */ handler.parameterize(stmt); // eg1: 返回org.apache.ibatis.logging.jdbc.PreparedStatementLogger@2e570ded return stmt; }
在handle.prepare()中执行编译语句,调用的是原生的JDBC进行预编译
/** * 使用prepareStatement方法,对SQL编译并设置入参 * * @param handler * @param statementLog * @return * @throws SQLException */ // eg1: handler=RoutingStatementHandler private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; /** 获得Connection实例 */ Connection connection = getConnection(statementLog); // eg1: handler=RoutingStatementHandler /** 第一步:调用了StatementHandler的prepared进行了【sql的预编译】 */ stmt = handler.prepare(connection, transaction.getTimeout()); /** 第二步:通过PreparedStatementHandler的parameterize来给【sql设置入参】 */ handler.parameterize(stmt); // eg1: 返回org.apache.ibatis.logging.jdbc.PreparedStatementLogger@2e570ded return stmt; }
-
第一步:调用了StatementHandler的prepared进行了【sql的预编译】
// eg1: delegate=PreparedStatementHandler /** * 执行预编译语句 */ @Override public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException { ErrorContext.instance().sql(boundSql.getSql()); Statement statement = null; try { // eg1: 调用PreparedStatementHandler的instantiateStatement 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); } } ```
-
第二步:通过PreparedStatementHandler的parameterize来给【sql设置入参】
// eg1: org.apache.ibatis.logging.jdbc.PreparedStatementLogger@2e570ded /** * 针对预处理语句,设置入参 */ @Override public void setParameters(PreparedStatement ps) { ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId()); // eg1: parameterMappings[0] = ParameterMapping{property='id', mode=IN, javaType=class java.lang.Long, jdbcType=null, numericScale=null, resultMapId='null', jdbcTypeName='null', expression='null'} List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); if (parameterMappings != null) { // eg1: parameterMappings.size() = 1 for (int i = 0; i < parameterMappings.size(); i++) { ParameterMapping parameterMapping = parameterMappings.get(i); // eg1: parameterMapping.getMode() = IN if (parameterMapping.getMode() != ParameterMode.OUT) { Object value; // eg1: propertyName="id" String propertyName = parameterMapping.getProperty(); // eg1: boundSql.hasAdditionalParameter(propertyName) = false if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params value = boundSql.getAdditionalParameter(propertyName); } // eg1: parameterObject = 2 else if (parameterObject == null) { value = null; } // eg1: parameterObject.getClass() = java.lang.Long.class 返回ture else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { // eg1: value = parameterObject = 2L value = parameterObject; } else { MetaObject metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName); } // eg1: typeHandler=class java.lang.Long TypeHandler typeHandler = parameterMapping.getTypeHandler(); // eg1: jdbcType=null JdbcType jdbcType = parameterMapping.getJdbcType(); // eg1: value=2L jdbcType=null if (value == null && jdbcType == null) { jdbcType = configuration.getJdbcTypeForNull(); } try { // eg1: typeHandler=BaseTypeHandler /** 针对预处理语句,设置入参 */ typeHandler.setParameter(ps, i + 1, value, jdbcType); } catch (TypeException e) { throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e); } catch (SQLException e) { throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e); } } } } }
-
开始执行真正的JDBC查询操作,将包装好的Statement通过StatementHandler来执行,并把结果传递给resultHandler
// eg1: delegate = PreparedStatementHandler resultHandler = null @Override public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { /** 最终还是使用JDBC去进行数据操作 */ PreparedStatement ps = (PreparedStatement) statement; /** 执行查询操作 */ ps.execute(); // eg1: 封装结果集 resultSetHandler=DefaultResultSetHandler /** 将结果集进行封装 */ return resultSetHandler.handleResultSets(ps); }
-
执行查询完成后,会将结果集进行处理封装resultSetHandler.handleResultSets(ps);
,这里逻辑看第六阶段,最终返回一个list(POJO)并维护到一级缓存中,至此就完成了DB查询操作
第五阶段基本流程如下:
- 通过queryFromDatabase()方法执行查询操作
- 调用SimpleExecutor的doQuery()方法
- 利用Configuration构建一个StatementHandler
- 根据构建的StatementHandler执行prepare()方法对sql预编译
- 调用DefaultParameterHandler实例的setParameters()方法对预处理的语句设置入参(也就是处理
?
的占位符) - 调用构建的StatementHandler(PreparedStatementHandler)执行查询操作
ps.execute();
,然后将结果集处理并返回return resultSetHandler.handleResultSets(ps);
- 根据获取的list集合维护到一级缓存中并返回处理后的结果POJO
第六阶段:针对ResultSet结果集转换为POJO
在上一个阶段,我们将结果集查出了,需要进行处理
/** 将结果集进行封装 */
return resultSetHandler.handleResultSets(ps);
我们来看下handleResultSets(Statement stmt)
方法:
/**
* 处理数据库操作的结果集
*/
// eg1: 执行到这里
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<>();
int resultSetCount = 0;
/** 首先:获得执行后的结果集,并封装到ResultSetWrapper */
ResultSetWrapper rsw = getFirstResultSet(stmt);
/** 其次:如果rsw != null && resultMapCount < 1,则抛异常ExecutorException */
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size(); // eg1: resultMapCount = 1
validateResultMapsCount(rsw, resultMapCount);
// eg1: rsw不为空 resultMapCount=1 resultSetCount=0
/** 第三步:处理结果集 */
while (rsw != null && resultMapCount > resultSetCount) {
// eg1: ResultMap resultMap=resultMaps.get(0);
ResultMap resultMap = resultMaps.get(resultSetCount);
/** 处理结果集, 存储在multipleResults中 */
handleResultSet(rsw, resultMap, multipleResults, null);
// eg1: rsw=null
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++; // eg1: 自增后resultSetCount=1
}
String[] resultSets = mappedStatement.getResultSets();
// eg1: resultSets = null
if (resultSets != null) {
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
// eg1: multipleResults.get(0).get(0) = User{id=2, name='muse2', age=24, userContacts=null}
/** 返回结果 */
return collapseSingleResultList(multipleResults);
}
-
第一步:获得执行后的结果集,并封装到ResultSetWrapper中
private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException { // eg1: rs != null /** 通过JDBC获得结果集ResultSet */ ResultSet rs = stmt.getResultSet(); while (rs == null) { if (stmt.getMoreResults()) { rs = stmt.getResultSet(); } else { /** * getUpdateCount()==-1,既不是结果集,又不是更新计数了.说明没的返回了。 * 如果getUpdateCount()>=0,则说明当前指针是更新计数(0的时候有可能是DDL指令)。 * 无论是返回结果集或是更新计数,那么则可能还继续有其它返回。 * 只有在当前指指针getResultSet()==null && getUpdateCount()==-1才说明没有再多的返回。 */ if (stmt.getUpdateCount() == -1) { // no more results. Must be no resultset break; } } } // eg1: rs不为空,则将结果集封装到ResultSetWrapper中 /** 将结果集ResultSet封装到ResultSetWrapper实例中 */ return rs != null ? new ResultSetWrapper(rs, configuration) : null; }
-
第二步:从MappedStatement中获得ResultMap集合(就是列和属性的映射map)
-
第三步:处理结果集
handleResultSet(rsw, resultMap, multipleResults, null);
,最终会将结果存在multipleResults中// eg1: parentMapping = null /** * 处理结果集 */ private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException { try { // eg1: parentMapping = null if (parentMapping != null) { handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping); } else { // eg1: resultHandler = null if (resultHandler == null) { // eg1: objectFactory = DefaultObjectFactory defaultResultHandler里面包含了一个空集合的ArrayList实例 /** 初始化ResultHandler实例,用于解析查询结果并存储于该实例对象中 */ DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory); /** 解析行数据 */ handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null); multipleResults.add(defaultResultHandler.getResultList()); } else { handleRowValues(rsw, resultMap, resultHandler, rowBounds, null); } } } finally { // eg1: /** 关闭ResultSet */ closeResultSet(rsw.getResultSet()); } } // eg1: parentMapping = null public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException { // eg1: resultMap.hasNestedResultMaps()=false /** 是否是聚合Nested类型的结果集 */ if (resultMap.hasNestedResultMaps()) { ensureNoRowBounds(); checkResultHandler(); handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping); } else { // eg1: parentMapping = null handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping); } } // eg1: parentMapping = null private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException { DefaultResultContext<Object> resultContext = new DefaultResultContext<>(); // eg1: skipRows里面没做什么事情 /** 将指针移动到rowBounds.getOffset()指定的行号,即:略过(skip)offset之前的行 */ skipRows(rsw.getResultSet(), rowBounds); // eg1: shouldProcessMoreRows(resultContext, rowBounds) = true rsw.getResultSet().next() = true while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) { /** 解析结果集中的鉴别器<discriminate/> */ ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null); /** 将数据库操作结果保存到POJO并返回 */ Object rowValue = getRowValue(rsw, discriminatedResultMap); // eg1: rowValue=User{id=2, name='muse2', age=24, userContacts=null} parentMapping = null /** 存储POJO对象到DefaultResultHandler中 */ storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet()); } }
handleRowValuesForSimpleResultMap()
方法中主要做了如下处理:- 将数据库操作结果保存到POJO并返回
在getRowValue()方法中调用了有对自动映射进行判断(shouldApplyAutomaticMappings()),如果开启了自动映射,会将列名与属性名进行映射,然后在结果集中取出对应的列名value,然后set到对应属性名中/** * 将数据库操作结果保存到POJO并返回 */ private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException { final ResultLoaderMap lazyLoader = new ResultLoaderMap(); /** 创建空的结果对象 */ Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null); // eg1: rowValue=User{id=null, name='null', age=null, userContacts=null} hasTypeHandlerForResultObject(rsw, resultMap.getType())=false if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) { /** 创建rowValue的metaObject */ final MetaObject metaObject = configuration.newMetaObject(rowValue); // eg1: foundValues = useConstructorMappings = false boolean foundValues = this.useConstructorMappings; // eg1: shouldApplyAutomaticMappings(resultMap, false) = true /** 是否应用自动映射 */ if (shouldApplyAutomaticMappings(resultMap, false)) { // eg1: applyAutomaticMappings(rsw, resultMap, metaObject, null)=true /** * 将查询出来的值赋值给metaObject中的POJO对象 */ foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues; // eg1: foundValues=true } // eg1: foundValues=true foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues; // eg1: lazyLoader.size()=0 foundValues=true foundValues = lazyLoader.size() > 0 || foundValues; // eg1: foundValues=true configuration.isReturnInstanceForEmptyRow()=false /** configuration.isReturnInstanceForEmptyRow() 当返回行的所有列都是空时,MyBatis默认返回null。当开启这个设置时,MyBatis会返回一个空实例。*/ rowValue = (foundValues || configuration.isReturnInstanceForEmptyRow()) ? rowValue : null; } return rowValue; // eg1: rowValue=User{id=2, name='muse2', age=24, userContacts=null} } // eg1: columnPrefix = null private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException { this.useConstructorMappings = false; // reset previous mapping result final List<Class<?>> constructorArgTypes = new ArrayList<>(); final List<Object> constructorArgs = new ArrayList<>(); /** 创建一个空的resultMap.getType()类型的实例对象 */ Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix); // eg1: resultObject=User{id=null, name='null', age=null, userContacts=null} // hasTypeHandlerForResultObject(rsw, resultMap.getType()) = false /** 判断resultMap.getType()是否存在TypeHandler */ if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) { final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings(); // eg1: propertyMappings={} for (ResultMapping propertyMapping : propertyMappings) { /** 如果是聚合查询并且配置了懒加载 */ if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) { resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs); break; } } } // eg1: resultObject=User{id=null, name='null', age=null, userContacts=null} constructorArgTypes.isEmpty()=true this.useConstructorMappings = (resultObject != null && !constructorArgTypes.isEmpty()); // eg1: useConstructorMappings = false return resultObject; // eg1: resultObject=User{id=null, name='null', age=null, userContacts=null} } // eg1: constructorArgTypes={} constructorArgs={} columnPrefix=null private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) throws SQLException { // eg1: resultType = vo.User.class /** 获得需要转换为POJO的类型 */ final Class<?> resultType = resultMap.getType(); /** 将POJO类型包装成MetaClass */ final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory); // eg1: constructorMappings = {} final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings(); // eg1: hasTypeHandlerForResultObject(rsw, resultType) = false /** 判断resultType是否存在TypeHandler */ if (hasTypeHandlerForResultObject(rsw, resultType)) { /** 创建原始的ResultObject,即:通过配置好的TypeHandler来创建 */ return createPrimitiveResultObject(rsw, resultMap, columnPrefix); } // eg1: constructorMappings.isEmpty()=true else if (!constructorMappings.isEmpty()) { /** 创建参数化的ResultObject */ return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix); } // eg1: resultType.isInterface()=false metaType.hasDefaultConstructor()=true else if (resultType.isInterface() || metaType.hasDefaultConstructor()) { // eg1: objectFactory=DefaultObjectFactory resultType=vo.User.class /** 使用objectFactory初始化User对象*/ return objectFactory.create(resultType); // eg1: 返回User{id=null, name='null', age=null, userContacts=null} } else if (shouldApplyAutomaticMappings(resultMap, false)) { return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix); } throw new ExecutorException("Do not know how to create an instance of " + resultType); }
// eg1: columnPrefix=null private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException { /** 创建自动映射 */ List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix); boolean foundValues = false; // eg1: autoMapping={UnMappedColumnAutoMapping("id", "id", LongTypeHandler@2397, false), // UnMappedColumnAutoMapping("name", "name", StringTypeHandler@2418, false), // UnMappedColumnAutoMapping("age", "age", IntegerTypeHandler@2433, false)} // 真正的赋值操作 if (autoMapping.size() > 0) { for (UnMappedColumnAutoMapping mapping : autoMapping) { // eg1: mapping.column="id" mapping.typeHandler=LongTypeHandler value=2L // eg1: mapping.column="name" mapping.typeHandler=StringTypeHandler value="muse2" // eg1: mapping.column="age" mapping.typeHandler=IntegerTypeHandler value=24 final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column); if (value != null) { // eg1: foundValues = true // eg1: foundValues = true // eg1: foundValues = true foundValues = true; } // eg1: configuration.isCallSettersOnNulls()=false mapping.primitive=false // eg1: configuration.isCallSettersOnNulls()=false mapping.primitive=false // eg1: configuration.isCallSettersOnNulls()=false mapping.primitive=false if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) { // eg1: mapping.property="id" value=2L // eg1: mapping.property="name" value="muse2" // eg1: mapping.property="age" value=24 metaObject.setValue(mapping.property, value); } } } return foundValues; // eg1: 返回true }
- 将数据库操作结果保存到POJO并返回
-
存储POJO对象到DefaultResultHandler和DefaultResultContext中
// eg1: rowValue=User{id=2, name='muse2', age=24, userContacts=null} parentMapping = null
/**
* 存储POJO对象到DefaultResultHandler中
*/
private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext,
Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {
// eg1: parentMapping = null
if (parentMapping != null) {
linkToParents(rs, parentMapping, rowValue);
} else {
// eg1: resultHandler里保存空size的ArrayList rowValue=User{id=2, name='muse2', age=24, userContacts=null}
/** 将结果存储到DefaultResultHandler中 */
callResultHandler(resultHandler, resultContext, rowValue);
}
}
// eg1: resultHandler里保存空size的ArrayList rowValue=User{id=2, name='muse2', age=24, userContacts=null}
/**
*
*/
@SuppressWarnings("unchecked" /* because ResultHandler<?> is always ResultHandler<Object>*/)
private void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext,
Object rowValue) {
resultContext.nextResultObject(rowValue);
// eg1: resultHandler=DefaultResultHandler
((ResultHandler<Object>) resultHandler).handleResult(resultContext);
}
至此,就完成最后的结果集处理操作
第六阶段主要流程如下:
- 调用handlerResultSet()方法处理结果集
- 获取到ResultSet并封装到ResultSetWrapper
- 然后处理结果集rsw和resultMap
handleResultMap(rsw, resultMap, multipleResults, null)
- 对行数据进行解析handlerRowValues()
- 利用反射创建空的POJO对象
- 将column和property进行自动映射或根据配置的ResultMap映射
- 根据映射的集合进行遍历,从ResultSet中获取值并set到POJO对象中
- 返回rowValue
- 将最终的结果存储在multipleResults集合中
- 对行数据进行解析handlerRowValues()
- 返回multipleResults
总结
大致的总流程如下图所示: