博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
MyBatis源码分析(四):SQL执行过程分析
阅读量:4570 次
发布时间:2019-06-08

本文共 16146 字,大约阅读时间需要 53 分钟。

一、获取Mapper接口的代理

根据上一节,Mybatis初始化之后,利用sqlSession(defaultSqlSession)的getMapper方法获取Mapper接口

1 @Override2 public 
T getMapper(Class
type) {3 return configuration.
getMapper(type, this);4 }

而调用configuration对象的getMapper方法

1 public 
T getMapper(Class
type, SqlSession sqlSession) {2 return mapperRegistry.getMapper(type, sqlSession);3 }

再次调用mapperRegister,注册mapper的类

1 public 
T getMapper(Class
type, SqlSession sqlSession) { 2 final MapperProxyFactory
mapperProxyFactory = (MapperProxyFactory
) knownMappers.get(type); 3 if (mapperProxyFactory == null) { 4 throw new BindingException("Type " + type + " is not known to the MapperRegistry."); 5 } 6 try { 7 return mapperProxyFactory.newInstance(sqlSession); 8 } catch (Exception e) { 9 throw new BindingException("Error getting mapper instance. Cause: " + e, e);10 }11 }

而mapperRegister根据传进来的mapper接口来创建MapperProxyFactory代理工厂对象,再用sqlSession参数创建Mapper的代理对象,这里运用的是JDK的动态代理,Proxy.newProxyInstance方法绑定mapper接口,第一个参数是类加载器,第二个参数是需要实现的接口数组,第三个是InvocationHandler接口,也就是交由InvocationHandler接口实现类MapperProxy里的invoke()方法去处理

1 @SuppressWarnings("unchecked")2   protected T newInstance(MapperProxy
mapperProxy) {3 return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);4 }5 6 public T newInstance(SqlSession sqlSession) {7 final MapperProxy
mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);8 return newInstance(mapperProxy);9 }

然后就这样给UserMapper赋予了一个代理对象

1 UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

二、使用Mapper代理对象进行查询操作

主代码调用代理对象查询,方法里面的参数为数据库字段的长整型id

1 user = userMapper.getUser(30L);

对应的mapper映射文件:

1 

使用Mapper代理对象,首先调用的是MapperProxy里面的invoke方法,传进三个主要的参数,分别是:代理对象、被调用的方法、方法的参数

1 @Override 2 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 3     try { 4       if (Object.class.equals(method.getDeclaringClass())) { 5         return method.invoke(this, args); 6       } else if (isDefaultMethod(method)) { 7         return invokeDefaultMethod(proxy, method, args); 8       } 9     } catch (Throwable t) {10       throw ExceptionUtil.unwrapThrowable(t);11     }12     final MapperMethod mapperMethod = cachedMapperMethod(method);13     return mapperMethod.execute(sqlSession, args);14 }

上面这段代码首先检查当前这个method是哪个类的方法,然后再判断有无默认方法,如果都没有则对方法进行缓存,最后对 SqlSession 进行的包装调用。

MapperMethod对SqlSession的操作进行了封装,来看其中的一段execute方法源码

1 public Object execute(SqlSession sqlSession, Object[] args) { 2     Object result; 3     switch (command.getType()) { 4       case INSERT: { 5         Object param = method.convertArgsToSqlCommandParam(args); 6         result = rowCountResult(sqlSession.insert(command.getName(), param)); 7         break; 8       } 9       case UPDATE: {10         Object param = method.convertArgsToSqlCommandParam(args);11         result = rowCountResult(sqlSession.update(command.getName(), param));12         break;13       }14       case DELETE: {15         Object param = method.convertArgsToSqlCommandParam(args);16         result = rowCountResult(sqlSession.delete(command.getName(), param));17         break;18       }19       case SELECT:20         if (method.returnsVoid() && method.hasResultHandler()) {21           executeWithResultHandler(sqlSession, args);22           result = null;23         } else if (method.returnsMany()) {24           result = executeForMany(sqlSession, args);25         } else if (method.returnsMap()) {26           result = executeForMap(sqlSession, args);27         } else if (method.returnsCursor()) {28           result = executeForCursor(sqlSession, args);29         } else {30           Object param = method.convertArgsToSqlCommandParam(args);31           result = sqlSession.selectOne(command.getName(), param);32           if (method.returnsOptional() &&33               (result == null || !method.getReturnType().equals(result.getClass()))) {34             result = Optional.ofNullable(result);35           }36         }37         break;38       case FLUSH:39         result = sqlSession.flushStatements();40         break;41       default:42         throw new BindingException("Unknown execution method for: " + command.getName());43     }44     if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {45       throw new BindingException("Mapper method '" + command.getName()46           + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");47     }48     return result;49 }

调用的mapper的查询操作,先看看上面这段的SELECT这一段代码。首先是看方法的返回值类型是否为空并且结果处理器resultHandler,有的话则执行实现的ResultHandler的方法;之后也是检查方法的参数和返回类型,有的话执行各种情况下的方法;都没有的话,把参数传进SQL命令中

1 public Object convertArgsToSqlCommandParam(Object[] args) {2       return paramNameResolver.getNamedParams(args);3 }

可以看到参数传递利用了ParamNameResolver,处理接口形式的参数,最后会把参数处放在一个map中,

1 public Object getNamedParams(Object[] args) { 2     final int paramCount = names.size(); 3     if (args == null || paramCount == 0) { 4       return null; 5     } else if (!hasParamAnnotation && paramCount == 1) { 6       return args[names.firstKey()]; 7     } else { 8       final Map
param = new ParamMap<>(); 9 int i = 0;10 for (Map.Entry
entry : names.entrySet()) {11 param.put(entry.getValue(), args[entry.getKey()]);12 // add generic param names (param1, param2, ...)13 final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);14 // ensure not to overwrite parameter named with @Param15 if (!names.containsValue(genericParamName)) {16 param.put(genericParamName, args[entry.getKey()]);17 }18 i++;19 }20 return param;21 }22 }

参数解析完后,MapperMethod使用sqlSession,执行一条操作:

1 result = sqlSession.selectOne(command.getName(), param);
1 @Override 2 public 
T selectOne(String statement, Object parameter) { 3 // Popular vote was to return null on 0 results and throw exception on too many. 4 List
list = this.selectList(statement, parameter); 5 if (list.size() == 1) { 6 return list.get(0); 7 } else if (list.size() > 1) { 8 throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size()); 9 } else {10 return null;11 }12 }
1 @Override 2 public 
List
selectList(String statement, Object parameter, RowBounds rowBounds) { 3 try { 4 MappedStatement ms = configuration.getMappedStatement(statement); 5 return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); 6 } catch (Exception e) { 7 throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); 8 } finally { 9 ErrorContext.instance().reset();10 }11 }

sqlSession的selectList最后使用到MappedStatement,这个MappedStatement是保存Mapper中一个SQL语句的结点。利用执行器进行查询,第二个参数是为了检查参数是不是一个集合;

1 private Object wrapCollection(final Object object) { 2     if (object instanceof Collection) { 3       StrictMap map = new StrictMap<>(); 4       map.put("collection", object); 5       if (object instanceof List) { 6         map.put("list", object); 7       } 8       return map; 9     } else if (object != null && object.getClass().isArray()) {10       StrictMap map = new StrictMap<>();11       map.put("array", object);12       return map;13     }14     return object;15 }

第三个参数是rowBounds逻辑分页方式,这里使用的是默认的;第四个是执行器的参数,这里是null。

然后跳到了CacheExecutor的query方法,它根据传进的MappedStatement参数获取BoundSql对象,ms中有mapper中的sql语句,放在SqlSource,然后根据传进来的参数组装成boundSql;之后生成一个对应二级缓存的key,

1 @Override2 public 
List
query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {3 BoundSql boundSql = ms.getBoundSql(parameterObject);4 CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);5 return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);6 }

但是Mybatis默认只开启了一级缓存,本例中并没有开启二级缓存,所以直接执行最后一个父类delegate.query方法,

1 public 
List
query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) 2 throws SQLException { 3 Cache cache = ms.getCache(); 4 if (cache != null) { 5 flushCacheIfRequired(ms); 6 if (ms.isUseCache() && resultHandler == null) { 7 ensureNoOutParams(ms, boundSql); 8 @SuppressWarnings("unchecked") 9 List
list = (List
) tcm.getObject(cache, key);10 if (list == null) {11 list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);12 tcm.putObject(cache, key, list); // issue #578 and #11613 }14 return list;15 }16 }17 return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);18 }

上面调用的是BaseExecutor中的query方法,此方法中的最重要的一段代码

1       list = resultHandler == null ? (List
) localCache.getObject(key) : null;2 if (list != null) {3 handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);4 } else {5 list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, 6 boundSql);7 }

因为我没有自己写的resultHandler类,所以直接执行

1     list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);

其方法源码为BaseExecutor抽象类中的queryFromDataBase

1 private 
List
queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { 2 List
list; 3 localCache.putObject(key, EXECUTION_PLACEHOLDER); 4 try { 5 list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); 6 } finally { 7 localCache.removeObject(key); 8 } 9 localCache.putObject(key, list);10 if (ms.getStatementType() == StatementType.CALLABLE) {11 localOutputParameterCache.putObject(key, parameter);12 }13 return list;14 }

queryFromDataBase中在深入,主要是第5行的doQuery方法:

1 @Override 2 public 
List
doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { 3 Statement stmt = null; 4 try { 5 Configuration configuration = ms.getConfiguration(); 6 StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); 7 stmt = prepareStatement(handler, ms.getStatementLog()); 8 return handler.query(stmt, resultHandler); 9 } finally {10 closeStatement(stmt);11 }12 }

StatementHadler是四大核心对象之一,它的任务就是和数据库对话。上面这段代码configuration.newStatementHandler方法使用了RoutingStatementHandler(采用的适配器模式)创建StatementHandler:

1 public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { 2  3     switch (ms.getStatementType()) { 4       case STATEMENT: 5         delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); 6         break; 7       case PREPARED: 8         delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); 9         break;10       case CALLABLE:11         delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);12         break;13       default:14         throw new ExecutorException("Unknown statement type: " + ms.getStatementType());15     }16 17 }

RoutingStatementHandler执行query方法

1 @Override2 public 
List
query(Statement statement, ResultHandler resultHandler) throws SQLException {3 return delegate.
query(statement, resultHandler);4 }

PreparedStatementHandler执行query方法

1 @Override2 public 
List
query(Statement statement, ResultHandler resultHandler) throws SQLException {3 PreparedStatement ps = (PreparedStatement) statement;4 ps.execute();5 return resultSetHandler.handleResultSets(ps);6 }

DefaultResultSetHandler执行handleResultSets方法,getFirstResultSet获取第一个结果集在于知道sql语句要操作到哪些元素数据(表的列),会获取到元数据名称、Java数据类型、JDBC数据类型,之后getResultMaps获取执行的sql配置的resultMap,之后一个resultMap对应一个结果集,依次遍历resultMap并处理结果集

1 @Override 2 public List handleResultSets(Statement stmt) throws SQLException { 3     ErrorContext.instance().activity("handling results").object(mappedStatement.getId()); 4  5     final List multipleResults = new ArrayList<>(); 6  7     int resultSetCount = 0; 8     ResultSetWrapper rsw = getFirstResultSet(stmt); 9 10     List
resultMaps = mappedStatement.getResultMaps();11 int resultMapCount = resultMaps.size();12 validateResultMapsCount(rsw, resultMapCount);13 while (rsw != null && resultMapCount > resultSetCount) { //一个resultMap对应一个结果集,依次遍历resultMap并处理结果集14 ResultMap resultMap = resultMaps.get(resultSetCount);15 handleResultSet(rsw, resultMap, multipleResults, null); // 处理结果集16 rsw = getNextResultSet(stmt);// 获取下一个结果集17 cleanUpAfterHandlingResultSet();18 resultSetCount++;19 }20 21 String[] resultSets = mappedStatement.getResultSets();22 if (resultSets != null) {23 while (rsw != null && resultSetCount < resultSets.length) {24 ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);25 if (parentMapping != null) {26 String nestedResultMapId = parentMapping.getNestedResultMapId();27 ResultMap resultMap = configuration.getResultMap(nestedResultMapId);28 handleResultSet(rsw, resultMap, null, parentMapping);29 }30 rsw = getNextResultSet(stmt);31 cleanUpAfterHandlingResultSet();32 resultSetCount++;33 }34 }35 36 return collapseSingleResultList(multipleResults); //把结果集转化为List37 }

DefaultResultSetHandler的getFirstResultSet方法

1 private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException { 2     ResultSet rs = stmt.getResultSet(); 3     while (rs == null) { // 没有结果集,也许是数据库驱动还没有返回第一个结果集 4       // move forward to get the first resultset in case the driver 5       // doesn't return the resultset as the first result (HSQLDB 2.1) 6       if (stmt.getMoreResults()) { // 尝试再一次获取结果集 7         rs = stmt.getResultSet(); 8       } else { 9         if (stmt.getUpdateCount() == -1) {  //表示驱动已经返回,没有更多结果,没有结果集10           // no more results. Must be no resultset11           break;12         }13       }14     }15     return rs != null ? new ResultSetWrapper(rs, configuration) : null; //不为空则返回结果集的包装16 }

ResultSetWrapper构造函数(包装结果集)

1 public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException { 2     super(); 3     this.typeHandlerRegistry = configuration.getTypeHandlerRegistry(); 4     this.resultSet = rs; 5     final ResultSetMetaData metaData = rs.getMetaData(); 6     final int columnCount = metaData.getColumnCount(); 7     for (int i = 1; i <= columnCount; i++) { // 设置结果集的元数据 8       columnNames.add(configuration.isUseColumnLabel() ? metaData.getColumnLabel(i) : metaData.getColumnName(i)); 9       jdbcTypes.add(JdbcType.forCode(metaData.getColumnType(i)));10       classNames.add(metaData.getColumnClassName(i));11     }12 }

把结果集转化为List

1 @SuppressWarnings("unchecked")2 private List collapseSingleResultList(List multipleResults) {3     return multipleResults.size() == 1 ? (List) multipleResults.get(0) : multipleResults;4 }

然后一层层传递回去,最后获得查询结果。(待续)

 

转载于:https://www.cnblogs.com/magic-sea/p/11204286.html

你可能感兴趣的文章
java经典算法四十题
查看>>
(转载) MTK flash
查看>>
Python 序列化之json、pickle
查看>>
python3 多线程笔记
查看>>
无尽的控件-GridView复合表头
查看>>
Luogu4726 【模板】多项式指数函数(NTT+多项式求逆)
查看>>
e3mall商城的归纳总结2之认识dubbo、zookeeper
查看>>
纯js实现图片上传
查看>>
嵌入式SQL
查看>>
HDOJ(HDU) 2133 What day is it(认识下Java的Calendar类---日期类)
查看>>
甲级1002 A+B for Polynomials (25)
查看>>
centos部署flask
查看>>
hdu 4507 吉哥系列故事——恨7不成妻
查看>>
C与C++ 无参函数的区别
查看>>
WPF DesiredSize & RenderSize
查看>>
快速开发第一个SpringBoot应用
查看>>
表中有A B C三列,用SQL语句实现:当A列大于B列时选择A列否则选择B列
查看>>
HTML video标签 兼容总结
查看>>
锡瓦塔内霍 墨西哥 / 巴克斯顿 /
查看>>
css+html应用实例1:滑动门技术的简单实现
查看>>