源码解析:mybatis调用链之执行sql语句并返回结果(一级缓存和二级缓存解析)
此流程以一个调用例子为说明,如下
TUser user = mapper.selectByPrimaryKey(1);
在mapper(代理对象)调用方法时,进入MapperProxy的invoke方法,
MapperProxy
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {//如果是Object本身的方法不增强
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
//从缓存中获取mapperMethod对象,如果缓存中没有,则创建一个,并添加到缓存中
final MapperMethod mapperMethod = cachedMapperMethod(method);
//调用execute方法执行sql
return mapperMethod.execute(sqlSession, args);
}
可以看到,在invoke方法中,会判断Object,不进行拦截增强,而是直接执行后返回。
如果是mapper本身的方法,则需要从缓存中获取MapperMethod对象,如果缓存中没有,则创建一个,并添加到缓存中
MapperProxy
private MapperMethod cachedMapperMethod(Method method) {
return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
MapperMethod有两个属性SqlCommand和MethodSignature,其作用分别为:
1、SqlCommand从configuration中获取方法的命名空间.方法名以及SQL语句的类型
2、MethodSignature封装mapper接口方法的相关信息(入参,返回类型);
MapperMethod
//从configuration中获取方法的命名空间.方法名以及SQL语句的类型
private final SqlCommand command;
//封装mapper接口方法的相关信息(入参,返回类型);
private final MethodSignature method;
......
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
在初始化对象实例之时,会初始化这两个属性SqlCommand和MethodSignature,
初始化SqlCommand对象,
MapperMethod.SqlCommand
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
final String methodName = method.getName();//获取方法名称
final Class<?> declaringClass = method.getDeclaringClass();
//从configuration中获取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 {//如果mappedStatement不为空
name = ms.getId();//获取sql的名称,命名空间+方法名称
type = ms.getSqlCommandType();//获取sql语句的类型
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
}
初始化SqlCommand,从上面代码可以出,除了方法名、Class对象外,还需要调用MapperMethod的resolveMappedStatement方法从configuration中获取MappedStatement实例对象,用于为SqlCommand属性name和type赋值。
MapperMethod.SqlCommand
//从configuration中获取mappedStatement
private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
Class<?> declaringClass, Configuration configuration) {
//sql语句的id为命名空间+方法名字
String statementId = mapperInterface.getName() + "." + methodName;
if (configuration.hasStatement(statementId)) {
return configuration.getMappedStatement(statementId);//从configuration中获取mappedStatement
} else if (mapperInterface.equals(declaringClass)) {
return null;
}
for (Class<?> superInterface : mapperInterface.getInterfaces()) {
if (declaringClass.isAssignableFrom(superInterface)) {
MappedStatement ms = resolveMappedStatement(superInterface, methodName,
declaringClass, configuration);
if (ms != null) {
return ms;
}
}
}
return null;
}
}
从上面代码可以看到,sql语句的id为命名空间+方法名字,因此通过命名空间+方法名字从configuration中获取MappedStatement。为SqlCommand的属性sql的名称name、sql语句的类型type赋值,
注:MappedStatement的具体解析以及生成过程请参考mybatis调用链之XMLStatementBuilder解析解析sql语句节点
初始化MethodSignature对象,
MapperMethod.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);
this.returnsOptional = Jdk.optionalExists && Optional.class.equals(this.returnType);
this.mapKey = getMapKey(method);
this.returnsMap = this.mapKey != null;
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
this.paramNameResolver = new ParamNameResolver(configuration, method);
}
初始化MethodSignature,封装mapper接口方法的相关信息(入参,返回类型);
至此,MapperMethod初始化完成,并存储到methodCache中缓存起来。methodCache的key是mapper接口中的某个方法的method对象,value是对应的MapperMethod,MapperMethod对象不记录任何状态信息,所以它可以在多个代理对象之间共享。
然后调用MapperMethod的execute方法执行sql,
MapperMethod
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
//根据sql语句类型以及接口返回的参数选择调用不同的
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:
if (method.returnsVoid() && method.hasResultHandler()) {//返回值为void
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {//返回值为集合或者数组
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {//返回值为map
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 = OptionalUtil.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语句类型以及接口返回的参数选择调用SqlSession不同的执行语句。
sql语句类型分为:
INSERT
UPDATE
DELETE
SELECT
????????分为返回值为void
????????返回值为集合或者数组
????????返回值为map
????????返回值为游标
????????返回为单一对象的情况
????????????????调用SqlSession的selectOne方法,SqlSession接口实现实例为DefaultSqlSession,
DefaultSqlSession
@Override
public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
List<T> list = this.<T>selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}
......
......
@Override
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//从configuration中获取要执行的sql语句的配置信息
MappedStatement ms = configuration.getMappedStatement(statement);
//通过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();
}
}
从configuration中获取要执行的sql语句的配置信息(MappedStatement),调用Executor的query方法,执行sql语句,并返回指定的结果集。
Executor的实现类有BatchExecutor、ReuseExecutor、SimpleExecutor和CachingExecutor
其中BatchExecutor、ReuseExecutor、SimpleExecutor都是继承于抽象类BaseExecutor。BaseExecutor实现了executor接口的大部分方法,主要提供了缓存管理(一级缓存)和事务管理的能力,其他子类需要实现的抽象方法为:doUpdate,doQuery等方法;
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
这里我们以Executor的实现类CachingExecutor为例,继续如下过程:
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);
}
该CachingExecutor.query方法关键步骤如下:
CachingExecutor.query——>> 1、获取sql语句信息,包括占位符,参数等信息
CachingExecutor.query——>> 2、拼装缓存的key值
CachingExecutor.query——>> 3、调用CachingExecutor的query重载方法。
CachingExecutor
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
//从MappedStatement中获取二级缓存
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);//从二级缓存中获取数据
if (list == null) {
//二级缓存为空,才会调用BaseExecutor.query
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);
}
????????可以看到,该方法封装了二级缓存的能力。
????????首先从MappedStatement中获取二级缓存,如果二级缓存未存在(表示未开启二级缓存,说明若是没有配置cache,则会关闭二级缓存),则直接从数据库中查询。
????????注意:若是Configuration的属性cacheEnabled设置为false,则执行器Executor不会增加二级缓存能力,这个时候,二级缓存也不开启。
????????上面方法表明,二级缓存由cache和key两个值唯一确定。这表明,二级缓存以每一个MappedStatement(也就是每一个具体的增删改查节点)为分界,再以入参进行区分。表示同一个mapper方法,入参不同,会有不同的缓存。另外即使不同的mapper方法查询数据是一致的,其缓存也是不同的。
????????若是二级缓存存在,则尝试根据key从二级缓存中获取数据,二级缓存为空,才会调用BaseExecutor.query从数据库中查询
BaseExecutor
protected PerpetualCache localCache;//一级缓存的实现,PerpetualCache
......
@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());
if (closed) {//检查当前executor是否关闭
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {//非嵌套查询,并且FlushCache配置为true,则需要清空一级缓存
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();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {//如果当前sql的一级缓存配置为STATEMENT,查询完既清空一集缓存
// issue #482
clearLocalCache();
}
}
return list;
}
可以看到,在BaseExecutor的query方法种,先尝试从一级缓存种获取数据,缓存未命中,则从数据库中加载数据。
不难看出,整体的mybatis查询过程,首先是去查询二级缓存,二级缓存若是没有数据,则会去查询一级缓存,一级缓存没有数据,这时,才会从数据库获取数据。
一级缓存的实现由PerpetualCache完成,缓存数据保存在其内的Map cache = new HashMap<>();属性中。这说明一级缓存在本质上是一个大的map,且由于PerpetualCache是Executor(BaseExecutor)的属性,而每一个Executor又属于一个SqlSession,所以说一级缓存属于SqlSession级别的,由key(是根据sql语句、入参等算出来的唯一值)值区分。
另外,我们可以看到,上述query方法是BaseExecutor抽象类中的方法,Executor的实现类,除了CachingExecutor外,都继承了BaseExecutor,且这些是西安类并未重写public List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException方法,因此,都会调用BaseExecutor的该query方法。
这表明mybatis并未提供一级缓存的关闭能力。
不过,在上述代码中表示,若是MappedStatement.isFlushCacheRequired配置为true,则需要清空一级缓存。这说明若在在sql语句的节点出处,配置flushCache为true,效果上相当于关闭了该sql的一级缓存。
除此之外,若在mybatis-config中配置setting值localCacheScope为STATEMENT,则查询完既清空一集缓存。这表明全局清空一级缓存,效果上相当于关闭了一级缓存。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
上述如果未从缓存中(包括一级和二级)获取到数据,则调用BaseExecutor的queryFromDatabase方法查询数据库,如下所示:
BaseExecutor
//真正访问数据库获取结果的方法
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 {
//调用抽象方法doQuery,方法查询数据库并返回结果,可选的实现包括:simple、reuse、batch
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;
}
BaseExecutor.queryFromDatabase方法有如下关键步骤:
BaseExecutor.queryFromDatabase——>> 1、在缓存中添加占位符
BaseExecutor.queryFromDatabase——>> 2、调用BaseExecutor的抽象方法doQuery,方法查询数据库并返回结果,可选的实现包括:SimpleExecutor、ReuseExecutor、BatchExecutor,在这里我们使用的是SimpleExecutor,代码如下所示:
//查询的实现
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);
//StatementHandler对象创建stmt,并使用parameterHandler对占位符进行处理
stmt = prepareStatement(handler, ms.getStatementLog());
//通过statementHandler对象调用ResultSetHandler将结果集转化为指定对象返回
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
这里主要分为三步:
1、创建StatementHandler对象
Configuration
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//创建RoutingStatementHandler对象,实际由statmentType来指定真实的StatementHandler来实现
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
????????创建RoutingStatementHandler对象,实际由statmentType来指定真实的StatementHandler来实现
RoutingStatementHandler
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//RoutingStatementHandler最主要的功能就是根据mappedStatment的配置,生成一个对应的StatementHandler对象并赋值给delegate
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());
}
}
????????我们这里以PreparedStatementHandler为例,展开讨论,新建PreparedStatementHandler对象,
PreparedStatementHandler
public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
}
????????进入PreparedStatementHandler父类BaseStatementHandler构造函数,
BaseStatementHandler
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
this.configuration = mappedStatement.getConfiguration();
this.executor = executor;
this.mappedStatement = mappedStatement;
this.rowBounds = rowBounds;
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.objectFactory = configuration.getObjectFactory();
if (boundSql == null) { // issue #435, get the key before calculating the statement
generateKeys(parameterObject);
boundSql = mappedStatement.getBoundSql(parameterObject);
}
this.boundSql = boundSql;
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}
????????可以看到,这里在ParameterHandler对象创建完后,使用了interceptorChain对ParameterHandler进行拦截器包装,与之相同的还有Executor和ResultSetHandler和StatementHandler,共称为mybatis拦截器拦截四大对象。
????????至此,StatementHandler对象创建完成,与此同时,还创建了ResultSetHandler和ParameterHandler对象。ResultSetHandler和ParameterHandler对象在StatementHandler对象内。
2、调用prepareStatement方法,StatementHandler对象创建stmt,并使用parameterHandler对占位符进行处理
SimpleExecutor
//创建Statement
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
//获取connection对象的动态代理,添加日志能力;
Connection connection = getConnection(statementLog);
//通过不同的StatementHandler,利用connection创建(prepare)Statement
stmt = handler.prepare(connection, transaction.getTimeout());
//使用parameterHandler处理占位符
handler.parameterize(stmt);
return stmt;
}
????????该方法做了以下几件事:
????????获取connection对象(数据库连接)的动态代理,添加日志能力;
????????通过不同的StatementHandler,利用connection创建(prepare)Statement;
RoutingStatementHandler
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
return delegate.prepare(connection, transactionTimeout);
}
BaseStatementHandler
//使用模板模式,定义了获取Statement的步骤,其子类实现实例化Statement的具体的方式;
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
//通过不同的子类实例化不同的Statement,分为三类:simple(statment)、prepare(prepareStatement)、callable(CallableStatementHandler)
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);
}
}
????????通过不同的子类实例化不同的Statement,分为三类:simple(statment)、prepare(prepareStatement)、callable(CallableStatementHandler);
????????我们这里以PreparedStatement为例,
PreparedStatementHandler
//使用底层的prepareStatement对象来完成对数据库的操作
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
//根据mappedStatement.getKeyGenerator字段,创建prepareStatement
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {//对于insert语句
String[] keyColumnNames = mappedStatement.getKeyColumns();
if (keyColumnNames == null) {
//返回数据库生成的主键
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
} else {
//返回数据库生成的主键填充至keyColumnNames中指定的列
return connection.prepareStatement(sql, keyColumnNames);
}
} else if (mappedStatement.getResultSetType() != null) {
//设置结果集是否可以滚动以及其游标是否可以上下移动,设置结果集是否可更新
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
//创建普通的prepareStatement对象
return connection.prepareStatement(sql);
}
}
通过Connection对象的prepareStatement方法创建Statement,
在这里,实际上执行的是Connection的代理对象,由于我们使用了ConnectionLogger(代理类,实现了InvocationHandler接口)对Connection对象进行了拦截,进入ConnectionLogger执行拦截方法。
//对连接的增强
public Object invoke(Object proxy, Method method, Object[] params)
throws Throwable {
try {
//如果是从Obeject继承的方法直接忽略
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, params);
}
//如果是调用prepareStatement、prepareCall、createStatement的方法,打印要执行的sql语句
//并返回prepareStatement的代理对象,让prepareStatement也具备日志能力,打印参数
if ("prepareStatement".equals(method.getName())) {
if (isDebugEnabled()) {
debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);//打印sql语句
}
PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);//创建代理对象
return stmt;
} else if ("prepareCall".equals(method.getName())) {
if (isDebugEnabled()) {
debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);//打印sql语句
}
PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);//创建代理对象
stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
return stmt;
} else if ("createStatement".equals(method.getName())) {
Statement stmt = (Statement) method.invoke(connection, params);
stmt = StatementLogger.newInstance(stmt, statementLog, queryStack);//创建代理对象
return stmt;
} else {
return method.invoke(connection, params);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
在这里,由于我们拦截的方法为PreparedStatement,因此,执行prepareStatement方法,返回PreparedStatement,并通过PreparedStatementLogger创建PreparedStatement的代理对象,为PreparedStatement增加日志功能。
PreparedStatementLogger
public static PreparedStatement newInstance(PreparedStatement stmt, Log statementLog, int queryStack) {
InvocationHandler handler = new PreparedStatementLogger(stmt, statementLog, queryStack);
ClassLoader cl = PreparedStatement.class.getClassLoader();
return (PreparedStatement) Proxy.newProxyInstance(cl, new Class[]{PreparedStatement.class, CallableStatement.class}, handler);
}
????????使用parameterHandler处理占位符;
????????调用StatementHandler的parameterize方法,
RoutingStatementHandler
@Override
public void parameterize(Statement statement) throws SQLException {
delegate.parameterize(statement);
}
PreparedStatementHandler
//使用parameterHandler对sql语句的占位符进行处理
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
可以看到,这里使用ParameterHandler对sql语句的占位符进行处理(这里的parameterHandler在前面创建过,为四大对象之一,为StatementHandler的属性变量。)
DefaultParameterHandler
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
//从boundSql中获取sql语句的占位符对应的参数信息
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
//遍历这个参数列表,把参数设置到PreparedStatement中
if (parameterMappings != null) {
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {//对于存储过程中的参数不处理
Object value;//绑定的实参
String propertyName = parameterMapping.getProperty();//参数的名字
if (boundSql.hasAdditionalParameter(propertyName)) { // 获取对应的实参值
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();//从parameterMapping中获取typeHandler对象
JdbcType jdbcType = parameterMapping.getJdbcType();//获取参数对应的jdbcType
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
//为statment中的占位符绑定参数
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);
}
}
}
}
}
3、通过statementHandler对象调用ResultSetHandler将结果集转化为指定对象返回
????????调用StatementHandler的query方法,从数据中心查询,
RoutingStatementHandler
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
return delegate.<E>query(statement, resultHandler);
}
PreparedStatementHandler
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
执行PreparedStatement的execute方法,实际上,是执行PreparedStatement的代理对象,由于我们使用PreparedStatementLogger对该对象进行代理,进入PreparedStatementLogger代理类,执行invoke方法,
PreparedStatementLogger
public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, params);
}
if (EXECUTE_METHODS.contains(method.getName())) {
if (isDebugEnabled()) {
debug("Parameters: " + getParameterValueString(), true);
}
clearColumnInfo();
if ("executeQuery".equals(method.getName())) {
ResultSet rs = (ResultSet) method.invoke(statement, params);
return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
} else {
return method.invoke(statement, params);
}
} else if (SET_METHODS.contains(method.getName())) {
if ("setNull".equals(method.getName())) {
setColumn(params[0], null);
} else {
setColumn(params[0], params[1]);
}
return method.invoke(statement, params);
} else if ("getResultSet".equals(method.getName())) {
ResultSet rs = (ResultSet) method.invoke(statement, params);
return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
} else if ("getUpdateCount".equals(method.getName())) {
int updateCount = (Integer) method.invoke(statement, params);
if (updateCount != -1) {
debug(" Updates: " + updateCount, false);
}
return updateCount;
} else {
return method.invoke(statement, params);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
????????查询完结果后,通过ResultSetHandler.handleResultSets处理结果集。结果集存储在Statement中。
DefaultResultSetHandler
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;
//statment可能返回多个结果集对象,这里先取出第一个结果集
ResultSetWrapper rsw = getFirstResultSet(stmt);
//获取结果集对应resultMap,本质就是获取字段与java属性的映射规则
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);//结果集和resultMap不能为空,为空抛出异常
while (rsw != null && resultMapCount > resultSetCount) {
//获取当前结果集对应的resultMap
ResultMap resultMap = resultMaps.get(resultSetCount);
//根据映射规则(resultMap)对结果集进行转化,转换成目标对象以后放入multipleResults中
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);//获取下一个结果集
cleanUpAfterHandlingResultSet();//清空nestedResultObjects对象
resultSetCount++;
}
//获取多结果集。多结果集一般出现在存储过程的执行,存储过程返回多个resultset,
//mappedStatement.resultSets属性列出多个结果集的名称,用逗号分割;
//多结果集的处理不是重点,暂时不分析
String[] resultSets = mappedStatement.getResultSets();
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++;
}
}
return collapseSingleResultList(multipleResults);
}
????????然后,获取结果集对应resultMap,本质就是获取字段与java属性的映射规则
????????ResultSetHandler.handleResultSets——>> 2、获取结果集对应resultMap,本质就是获取字段与java属性的映射规则
????????ResultSetHandler.handleResultSets——>> 3、获取当前结果集对应的resultMap
????????ResultSetHandler.handleResultSets——>> 4、根据映射规则(resultMap)对结果集进行转化,转换成目标对象以后放入multipleResults中
DefaultResultSetHandler
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {
if (parentMapping != null) {//处理多结果集的嵌套映射
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else {
if (resultHandler == null) {//如果resultHandler为空,实例化一个人默认的resultHandler
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
//对ResultSet进行映射,映射结果暂存在resultHandler中
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
//将暂存在resultHandler中的映射结果,填充到multipleResults
multipleResults.add(defaultResultHandler.getResultList());
} else {
//使用指定的rusultHandler进行转换
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
// issue #228 (close resultsets)
//调用resultset.close()关闭结果集
closeResultSet(rsw.getResultSet());
}
}
????????DefaultResultSetHandler.handleResultSet分为以下关键步骤:
????????DefaultResultSetHandler.handleResultSet——>> 1、处理多结果集的嵌套映射
????????DefaultResultSetHandler.handleResultSet——>> 2、如果resultHandler为空,实例化一个默认的resultHandler
????????DefaultResultSetHandler.handleResultSet——>> 3、该方法,对ResultSet进行映射,映射结果暂存在resultHandler中
DefaultResultSetHandler
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
if (resultMap.hasNestedResultMaps()) {//处理有嵌套resultmap的情况
ensureNoRowBounds();
checkResultHandler();
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {//处理没有嵌套resultmap的情况
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
????????DefaultResultSetHandler.handleRowValues分为以下几个关键步骤:
????????DefaultResultSetHandler.handleRowValues——>> 1、处理有嵌套resultmap的情况
DefaultResultSetHandler
private void handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
final DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
skipRows(rsw.getResultSet(), rowBounds);
Object rowValue = previousRowValue;
while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
final ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
final CacheKey rowKey = createRowKey(discriminatedResultMap, rsw, null);
Object partialObject = nestedResultObjects.get(rowKey);
// issue #577 && #542
if (mappedStatement.isResultOrdered()) {
if (partialObject == null && rowValue != null) {
nestedResultObjects.clear();
storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
}
rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);
} else {
rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);
if (partialObject == null) {
storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
}
}
}
if (rowValue != null && mappedStatement.isResultOrdered() && shouldProcessMoreRows(resultContext, rowBounds)) {
storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
previousRowValue = null;
} else if (rowValue != null) {
previousRowValue = rowValue;
}
}
????????DefaultResultSetHandler.handleRowValues——>> 2、处理没有嵌套resultmap的情况
DefaultResultSetHandler
//简单映射处理
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
throws SQLException {
//创建结果上下文,所谓的上下文就是专门在循环中缓存结果对象的
DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
//1.根据分页信息,定位到指定的记录
skipRows(rsw.getResultSet(), rowBounds);
//2.shouldProcessMoreRows判断是否需要映射后续的结果,实际还是翻页处理,避免超过limit
while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
//3.进一步完善resultMap信息,主要是处理鉴别器的信息
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
//4.读取resultSet中的一行记录并进行映射,转化并返回目标对象
Object rowValue = getRowValue(rsw, discriminatedResultMap);
//5.保存映射结果对象
storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
}
}
????????DefaultResultSetHandler. handleRowValuesForSimpleResultMap方法主要分为以下几种:
????????DefaultResultSetHandler. handleRowValuesForSimpleResultMap——>> 1、根据分页信息,定位到指定的记录
????????DefaultResultSetHandler. handleRowValuesForSimpleResultMap——>> 2、shouldProcessMoreRows判断是否需要映射后续的结果,实际还是翻页处理,避免超过limit
????????DefaultResultSetHandler. handleRowValuesForSimpleResultMap——>> 3、进一步完善resultMap信息,主要是处理鉴别器的信息
????????DefaultResultSetHandler. handleRowValuesForSimpleResultMap——>> 4、读取resultSet中的一行记录并进行映射,转化并返回目标对象
DefaultResultSetHandler
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
//4.1 根据resultMap的type属性,实例化目标对象
Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
//4.2 对目标对象进行封装得到metaObjcect,为后续的赋值操作做好准备
final MetaObject metaObject = configuration.newMetaObject(rowValue);
boolean foundValues = this.useConstructorMappings;//取得是否使用构造函数初始化属性值
if (shouldApplyAutomaticMappings(resultMap, false)) {//是否使用自动映射
//4.3一般情况下 autoMappingBehavior默认值为PARTIAL,对未明确指定映射规则的字段进行自动映射
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
}
//4.4 映射resultMap中明确指定需要映射的列
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
foundValues = lazyLoader.size() > 0 || foundValues;
//4.5 如果没有一个映射成功的属性,则根据<returnInstanceForEmptyRow>的配置返回null或者结果对象
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
}
return rowValue;
}
????????DefaultResultSetHandler.getRowValue方法中分为以下几个关键步骤:
????????DefaultResultSetHandler.getRowValue ——>> 1、根据resultMap的type属性,实例化目标对象
????????DefaultResultSetHandler.getRowValue ——>> 2、对目标对象进行封装得到metaObjcect,为后续的赋值操作做好准备
????????DefaultResultSetHandler.getRowValue ——>> 3、一般情况下 autoMappingBehavior默认值为PARTIAL,对未明确指定映射规则的字段进行自动映射
DefaultResultSetHandler
//对未明确指定映射规则的字段进行自动映射
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
//获取resultSet中存在的,但是ResultMap中没有明确映射的列,填充至autoMapping中
List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
boolean foundValues = false;
if (!autoMapping.isEmpty()) {
//遍历autoMapping,通过自动匹配的方式为属性复制
for (UnMappedColumnAutoMapping mapping : autoMapping) {
//通过typeHandler从resultset中拿值
final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
if (value != null) {
foundValues = true;
}
if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
// gcode issue #377, call setter on nulls (value is not 'found')
//通过metaObject给属性赋值
metaObject.setValue(mapping.property, value);
}
}
}
return foundValues;
}
????????DefaultResultSetHandler.getRowValue ——>> 4、映射resultMap中明确指定需要映射的列
DefaultResultSetHandler
private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
throws SQLException {
//从resultMap中获取明确需要转换的列名集合
final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
boolean foundValues = false;
//获取ResultMapping集合
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
for (ResultMapping propertyMapping : propertyMappings) {
String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);//获得列名,注意前缀的处理
if (propertyMapping.getNestedResultMapId() != null) {
// the user added a column attribute to a nested result map, ignore it
//如果属性通过另外一个resultMap映射,则忽略
column = null;
}
if (propertyMapping.isCompositeResult()//如果是嵌套查询,column={prop1=col1,prop2=col2}
|| (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))//基本类型映射
|| propertyMapping.getResultSet() != null) {//嵌套查询的结果
//获得属性值
Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
// issue #541 make property optional
//获得属性名称
final String property = propertyMapping.getProperty();
if (property == null) {//属性名为空跳出循环
continue;
} else if (value == DEFERED) {//属性名为DEFERED,延迟加载的处理
foundValues = true;
continue;
}
if (value != null) {
foundValues = true;
}
if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
// gcode issue #377, call setter on nulls (value is not 'found')
//通过metaObject为目标对象设置属性值
metaObject.setValue(property, value);
}
}
}
return foundValues;
}
????????DefaultResultSetHandler.getRowValue ——>> 5、如果没有一个映射成功的属性,则根据的配置返回null或者结果对象
????????DefaultResultSetHandler.getRowValue ——>> end
????????DefaultResultSetHandler. handleRowValuesForSimpleResultMap——>> 5、保存映射结果对象
DefaultResultSetHandler
//保存映射结果对象
private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {
if (parentMapping != null) {//如果是嵌套结果或嵌套查询,将对象保存至父对象
linkToParents(rs, parentMapping, rowValue);
} else {//普通映射则把对象保存至resultHandler和resultContext
callResultHandler(resultHandler, resultContext, rowValue);
}
}
????????DefaultResultSetHandler.storeObject关键步骤:
????????DefaultResultSetHandler.storeObject ——>> 1、如果是嵌套结果或嵌套查询,将对象保存至父对象
????????DefaultResultSetHandler.storeObject ——>> 2、普通映射则把对象保存至resultHandler和resultContext
DefaultResultSetHandler
@SuppressWarnings("unchecked" /* because ResultHandler<?> is always ResultHandler<Object>*/)
private void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue) {
resultContext.nextResultObject(rowValue);
((ResultHandler<Object>) resultHandler).handleResult(resultContext);
}
????????在callResultHandler方法中,可以看到resultContex的作用是暂存映射结果对象,之后会放入DefaultResultHandler容器当中
????????DefaultResultSetHandler.storeObject ——>> end
????????DefaultResultSetHandler. handleRowValuesForSimpleResultMap——>> end
????????DefaultResultSetHandler.handleResultSet——>> 4、将暂存在resultHandler中的映射结果,填充到multipleResults
????????DefaultResultSetHandler.handleResultSet——>> 5、当配置指定的rusultHandler视,使用指定的rusultHandler进行转换
????????DefaultResultSetHandler.handleResultSet——>> 6、调用resultset.close()关闭结果集
????????DefaultResultSetHandler.handleResultSet——>> end
????????ResultSetHandler.handleResultSets——>> 5、获取下一个结果集
DefaultResultSetHandler
private ResultSetWrapper getNextResultSet(Statement stmt) {
// Making this method tolerant of bad JDBC drivers
try {
if (stmt.getConnection().getMetaData().supportsMultipleResultSets()) {
// Crazy Standard JDBC way of determining if there are more results
if (!(!stmt.getMoreResults() && stmt.getUpdateCount() == -1)) {
ResultSet rs = stmt.getResultSet();
if (rs == null) {
return getNextResultSet(stmt);
} else {
return new ResultSetWrapper(rs, configuration);
}
}
}
} catch (Exception e) {
// Intentionally ignored.
}
return null;
}
????????ResultSetHandler.handleResultSets——>> 5、获取下一个结果集
????????ResultSetHandler.handleResultSets——>> 6、获取多结果集。多结果集一般出现在存储过程的执行,存储过程返回多个resultset,mappedStatement.resultSets属性列出多个结果集的名称,用逗号分割;多结果集的处理不是重点,暂时不分析。
????????ResultSetHandler.handleResultSets——>> end
????????至此,返回调用结果,查询调用链结束。
????????BaseExecutor.queryFromDatabase——>> 3、在缓存中删除占位符
????????BaseExecutor.queryFromDatabase——>> 4、将真正的结果对象添加到一级缓存
PerpetualCache
......
private Map<Object, Object> cache = new HashMap<>();
......
@Override
public void putObject(Object key, Object value) {
cache.put(key, value);
}
????????可以看到,一级缓存使用的是PerpetualCache,具体存储在属性cache(cache是一个map)中。
????????BaseExecutor.queryFromDatabase——>> end
????????CachingExecutor.query——>> end
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!