Mybatis实现【4】-查询解析(一次SQL查询的源码分析)
2014-06-18 16:29
791 查看
执行流程图:
一、在Service层调用Mapper Interface中的方法实现对数据库的操作
二、MapperProxy将拦截所有调用数据库的操作,所以实际上调用的是MapperProxy中的方法。
三、MapperMethod的execute方法如下:
四、前面中MyBatis返回给应用的DefaultSqlSession。
DefaultSqlSession里面有各种各样的SQL执行方法,主要用于SQL操作的对外接口,它会调用执行器执行实际的SQL语句。
1、selectOne方法
它实际上调用selectList方法。该方法如下:
2、执行器执行过程
2.1、默认使用的SimpleExecutor。根据调用链,最终执行的是SimpleExecutor的doQuery方法,如下:
2.2、StatementHandler
2.2.1、RoutingStatementHandler
2.3、prepareStatement方法具体内容
该系列文章参考如下书籍及文章:
《Java Persistence with MyBatis 》
《http://www.cnblogs.com/hzhuxin/p/3349836.html》
《http://www.iteye.com/topic/1112327》
《http://www.iteye.com/blogs/subjects/mybatis_internals》
《http://denger.me/2011/05/mybatis-and-spring-interface-integrated/》
一、在Service层调用Mapper Interface中的方法实现对数据库的操作
二、MapperProxy将拦截所有调用数据库的操作,所以实际上调用的是MapperProxy中的方法。
//当调用Mapper的方法时,实际上将由MapperProxy中的invoke方法处理 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } //获取MapperMethod类 final MapperMethod mapperMethod = cachedMapperMethod(method); //执行MapperMethod,并返回结果 return mapperMethod.execute(sqlSession, args); } //从缓存中或则实例化一个MapperMethod类,用于处理数据库操作 private MapperMethod cachedMapperMethod(Method method) { MapperMethod mapperMethod = methodCache.get(method); if (mapperMethod == null) { mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()); methodCache.put(method, mapperMethod); } return mapperMethod; }
三、MapperMethod的execute方法如下:
public Object execute(SqlSession sqlSession, Object[] args) { Object result; if (SqlCommandType.INSERT == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.insert(command.getName(), param)); } else if (SqlCommandType.UPDATE == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.update(command.getName(), param)); } else if (SqlCommandType.DELETE == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.delete(command.getName(), param)); } else if (SqlCommandType.SELECT == command.getType()) { 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 { //图示调用的是selectOne Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); } } else { 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; }
四、前面中MyBatis返回给应用的DefaultSqlSession。
DefaultSqlSession里面有各种各样的SQL执行方法,主要用于SQL操作的对外接口,它会调用执行器执行实际的SQL语句。
1、selectOne方法
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; } }
它实际上调用selectList方法。该方法如下:
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { try { //根据SQL的ID到配置信息中找对应的MappedStatement MappedStatement ms = configuration.getMappedStatement(statement); //调用执行器的query方法 List<E> result = executor.<E>query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); return result; } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
2、执行器执行过程
2.1、默认使用的SimpleExecutor。根据调用链,最终执行的是SimpleExecutor的doQuery方法,如下:
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); //new一个StatementHandler对象,该类主要用于处理一次SQL操作 StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql); //预处理StatementHandler对象,得到Statement对象 stmt = prepareStatement(handler, ms.getStatementLog()); //执行sql,并对执行结果进行处理 return handler.<E>query(stmt, resultHandler); } finally { closeStatement(stmt); } }
2.2、StatementHandler
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { //获得一个RoutingStatementHandler对象 StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); //为StatementHandler绑定拦截器插件 statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); return statementHandler; }
2.2.1、RoutingStatementHandler
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { //根据MappedStatement对象的statementType来创建不同的StatementHandler。StatementType有STATEMENT、PREPARED和CALLABLE三种类型,和JDBC类型对应 switch (ms.getStatementType()) { case STATEMENT: delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; case PREPARED: delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; case CALLABLE: delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; default: throw new ExecutorException("Unknown statement type: " + ms.getStatementType()); } }
2.3、prepareStatement方法具体内容
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; Connection connection = getConnection(statementLog); //从链接中获取Statement对象 stmt = handler.prepare(connection); //处理预编译的传入对象 handler.parameterize(stmt); return stmt; }
该系列文章参考如下书籍及文章:
《Java Persistence with MyBatis 》
《http://www.cnblogs.com/hzhuxin/p/3349836.html》
《http://www.iteye.com/topic/1112327》
《http://www.iteye.com/blogs/subjects/mybatis_internals》
《http://denger.me/2011/05/mybatis-and-spring-interface-integrated/》
相关文章推荐
- Mybatis3源码分析(17)-Sql解析执行-缓存的实现
- 【mybatis源码分析】原理分析之四:一次SQL查询的源码分析
- MyBatis原理分析之四:一次SQL查询的源码分析
- 原理分析之四:一次SQL查询的源码分析
- 原理分析之四:一次SQL查询的源码分析
- Mybatis底层原理学习(二):从源码角度分析一次查询操作过程
- 数据库中间件 Sharding-JDBC 源码分析 —— SQL 解析(三)之查询SQL
- 原理分析之四:一次SQL查询的源码分析
- Mybatis3源码分析(15)-Sql解析执行-Statement初始化和参数设置
- 数据库中间件 Sharding-JDBC 源码分析 —— SQL 解析(三)之查询SQL
- MyBatis-3.4.2-源码分析14:XML解析之sqlElement(context.evalNodes("/mapper/sql"))
- Mybatis3源码分析(13)-Sql解析执行-BoundSql的加载-2
- Mybatis3源码分析(12)-Sql解析执行-MetaObject
- Mybatis源码分析 之 sql解析
- MyBatis-3.4.2-源码分析18:XML解析之RoleMapper userMapper = sqlSession.getMapper(RoleMapper.class)
- Mybatis3源码分析(16)-Sql解析执行-结果集映射(ResultSetHandler)
- Spring mybatis源码篇章-NodeHandler实现类具体解析保存Dynamic sql节点信息
- mybatis源码解析 - 通过一个简单查询例子分析流程
- 原理分析之四:一次SQL查询的源码分析
- 通过源码分析MyBatis的缓存/Mybatis解析动态sql原理分析