1、首先在该处打上断点
因为mapper是代理对象, 进入代理类MapperProxy中的invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 判断是否是Object中的方法, method对象就是上面的getUser方法
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (method.isDefault()) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
// 主要进入该方法中
return mapperMethod.execute(sqlSession, args);
}
进入到MapperMethod类的execute方法, 因为是查询方法, 所以仅为SELECT分支
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);
// 这里可以发现, 底层仍然使用的是selectOne方法
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional()
&& (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
进入convetArgsToSqlCommandParam方法的ParamNameResolver类的getNamedParams方法
在下面代码之前, 要搞懂ParamNameResolver类中private final SortedMap names;的值是怎么来的. 是在ParamNameResolver构造器中初始化的
public ParamNameResolver(Configuration config, Method method) {
// 获取getUser方法的参数类型
final Class[] paramTypes = method.getParameterTypes();
// 获取getUser方法的注解
final Annotation[][] paramAnnotations = method.getParameterAnnotations();
final SortedMap map = new TreeMap();
int paramCount = paramAnnotations.length;
// get names from @Param annotations
for (int paramIndex = 0; paramIndex 12
} else {
final Map param = new ParamMap();
int i = 0;
for (Map.Entry entry : names.entrySet()) {
// 循环后: param这个map中存放的就是:
// {id=args[0], name=args[1]}-->{id=12, name="lisi"}
// 所以说@Param标注的参数, 底层仍然使用的是Map的方式, 采用#{key}来取值即可
param.put(entry.getValue(), args[entry.getKey()]);
// add generic param names (param1, param2, ...)
// 下面的常量就是:param字符串
final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
// ensure not to overwrite parameter named with @Param
if (!names.containsValue(genericParamName)) {
// 除了会存放上面的, param中还存放了{param1=12, param2="lisi"}
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
// {name=lisi, id=12, param1=12, param2=lisi}
// 如此一来我们就可以#{key}或者#{param1}...#{paramN}的方式来取值了
return param;
}
}
1、因为mapper是一个代理对象, 实现了InvocationHandler, 所以在执行目标方法之前要执行invoke方法, 所以进入到Invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 判断当前方法是不是Object中声明的方法
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (method.isDefault()) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
// method: public abstract com.sunny.domain.User com.sunny.mapper.UserMapper.getUserById(int)
// 将method包装为mybatis能识别的MapperMethod
final MapperMethod mapperMethod = cachedMapperMethod(method);
// 执行execute: args为传过来的参数12
return mapperMethod.execute(sqlSession, args);
}
2、进入MapperMethod的execute方法, 根据SQL语句, 选择SELECT分支
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
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()) {
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 {
// 这个将参数转换为sql命令参数的方法, 在后面会专门分析,多个参数转map的过程
// 如果args为一个参数直接返回, 多个参数泽包装为一个map的过程
Object param = method.convertArgsToSqlCommandParam(args);
// 下面进入该方法, SqlCommand封装了SQL语句的信息
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;
}
3、进入的DefaultSqlSession的selectOne方法
@Override
public T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
// 调用下面的方法
List list = this.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 List selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
@Override
public List selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// 通过configuration获取MappedStatement, 因为ms封装了CRUD标签的详细信息
// 并且包含了sqlSource的信息(如下图)
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();
}
}
// 在executor.query之前, 先判断传进来的参数是否是Collection类型
// 下面的逻辑就标明了, 在编写mapper.xml中 List --> #{list}; Array --> #{array}的取值方式
private Object wrapCollection(final Object object) {
if (object instanceof Collection) {
StrictMap 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 map = new StrictMap();
map.put("array", object);
return map;
}
return object;
}
4、进入的CachingExecutor的query方法
@Override
public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// 获取到sql语句的详细信息
// BoundSql: SQL绑定, 封装SQL语句和对应的参数信息。
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
BoundSql内容 5、再进入上面return query方法
@Override
public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
// 判断是否有二级缓存
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List list = (List) tcm.getObject(cache, key);
if (list == null) {
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
// 此时的delete就是你全局配置的那个执行器,这里没有配置默认为: SimpleExecutor
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
6、进入BaseExecutor的query方法
@Override
public List 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 list;
try {
queryStack++;
// 从一级缓存中拿, 这也印证了先从二级缓存拿, 然后再一级缓存
list = resultHandler == null ? (List) 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) {
// issue #482
clearLocalCache();
}
}
return list;
}
7、进入到queryFromDatabase方法
private List queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List list;
// 往一级缓存存放数据, value为占位符
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
// 当查询出数据后, 再将查出的数据作为value存放到map
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
8、进入到BaseExecutor的doQuery方法, 走的是子类SimpleExecutor的doQuery方法
@Override
public List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
// 原生JDBC的Statement
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
// 创建了StatementHandler对象: 这里为PreparedStatementHandler对象
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// 下面进入到RoutingStatementHandler来判断创建的StatementHandler
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
// 创建的statementHandler类型为PreparedStatementHandler,
// 会经过拦截器链进行包装增强(类似aop的功能)
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// 从MeppedStatement中获取StatementType的类型
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
// 如果没有设置, 默认为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());
}
}
// 可以在crud标签中设置, 默认为PREPARED预编译的方式
public enum StatementType {
STATEMENT, PREPARED, CALLABLE
}
// 在创建PreparedStatementHandler对象的时候, 使用super调用父类的构造器,
// 同时也将parameterHandler, resultSetHandler也创建了: 下面会使用到这俩对象
public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
}
// PreparedStatementHandler父类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;
// 创建下面俩对象之后, 仍然会经过拦截器链进行包装增强然后返回对象(类似aop的功能)
// statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}
9、返回到SimpleExecutor类doQuery方法, 并进入prepareStatement方法
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
// 拿到一个连接, 进行参数预编译设置到stmt中, handler:PrepareStatementHandler
stmt = handler.prepare(connection, transaction.getTimeout());
// 进行参数预编译
handler.parameterize(stmt);
return stmt;
}
10、进入到 PreparedStatementHandler的parameterize方法: 进行参数预编译
@Override
public void parameterize(Statement statement) throws SQLException {
// 会调用parameterHandler设置参数
parameterHandler.setParameters((PreparedStatement) statement);
}
11、进入DefaultParameterHandler的setParameters方法
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
List parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
for (int i = 0; i resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
18、进入到handleResultSet方法, 再进入handleRowValues方法, 再进入handleRowValuesForSimpleResultMap方法, 再进入getRowValue方法, 再进入applyAutomaticMappings方法
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
List autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
boolean foundValues = false;
if (!autoMapping.isEmpty()) {
for (UnMappedColumnAutoMapping mapping : autoMapping) {
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.setValue(mapping.property, value);
}
}
}
return foundValues;
}
19、在设置上面metaObject.setValue之前, 要进行数据库列类型和JavaBean类型做映射。
private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
throws SQLException {
if (propertyMapping.getNestedQueryId() != null) {
return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
} else if (propertyMapping.getResultSet() != null) {
addPendingChildRelation(rs, metaResultObject, propertyMapping); // TODO is that OK?
return DEFERRED;
} else {
final TypeHandler typeHandler = propertyMapping.getTypeHandler();
final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
// 同样采用typeHandler来处理参数映射
return typeHandler.getResult(rs, column);
}
}
// IntegerTypeHandler中的getNullableResult来获取columnName列的值
@Override
public Integer getNullableResult(ResultSet rs, String columnName)
throws SQLException {
int result = rs.getInt(columnName);
return result == 0 && rs.wasNull() ? null : result;
}
// 其他字段同理这样获取, 只是采用的TypeHandler不同, 然后都通过
// metaObject.setValue(mapping.property, value);设置给metaObject
最终取出数据设置给JavaBean对象
20、最后将查询到的数据赋值给JavaBean对象
查询流程总结:
1、获取sqlSessionFactory对象:
解析文件的每一个信息保存在Configuration中,返回包含Configuration的DefaultSqlSession;
注意:【MappedStatement】:代表一个增删改查的详细信息
2、获取sqlSession对象
返回一个DefaultSQlSession对象,包含Executor和Configuration;
这一步会创建Executor对象;
3、获取接口的代理对象(MapperProxy)
getMapper,使用MapperProxyFactory创建一个MapperProxy的代理对象
代理对象里面包含了,DefaultSqlSession(Executor)
4、执行增删改查方法
总结:
1、根据配置文件(全局,sql映射)初始化出Configuration对象
2、创建一个DefaultSqlSession对象,
他里面包含Configuration以及
Executor(根据全局配置文件中的defaultExecutorType创建出对应的Executor)
3、DefaultSqlSession.getMapper():拿到Mapper接口对应的MapperProxy;
4、MapperProxy里面有(DefaultSqlSession);
5、执行增删改查方法:
1)、调用DefaultSqlSession的增删改查(Executor);
2)、会创建一个StatementHandler对象。
(同时也会创建出ParameterHandler和ResultSetHandler)
3)、调用StatementHandler预编译参数以及设置参数值;
使用ParameterHandler来给sql设置参数
4)、调用StatementHandler的增删改查方法;
5)、ResultSetHandler封装结果
注意:
四大对象每个创建的时候都有一个interceptorChain.pluginAll(parameterHandler);