MyBatis源码学习笔记(十)SQL执行流程分析
2017-06-14 11:13
1146 查看
SQL执行流程分析
前面几篇文章,主要讲了对mybatis配置文件的解析,并且把他set到configuration这个对象中去,所以之后就要开始建立SqlSessionFactory了。@Test public void findStudentById() { //首先通过getSessionFactory()方法获取SqlSessionFactory SqlSessionFactory sqlSessionFactory = getSessionFactory(); //其次通过SqlSessionFactory中的openSession方法获取SqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); StudentDao studentDao = sqlSession.getMapper(StudentDao.class); Student student = studentDao.findStudentById("20140101"); List<Course> courseList = student.getCourseList(); for (Course course: courseList) { System.out.println(course.getId() + " " + course.getName()); } } //Mybatis 通过SqlSessionFactory获取SqlSession, 然后才能通过SqlSession与数据库进行交互 private static SqlSessionFactory getSessionFactory() { SqlSessionFactory sessionFactory = null; String resource = "xml/configuration.xml"; try { //而SqlSessionFactory是通过SqlSessionFactoryBuilder中的build方法去建立 sessionFactory = new SqlSessionFactoryBuilder().build(Resources .getResourceAsReader(resource)); } catch (IOException e) { e.printStackTrace(); } return sessionFactory; }
1、SqlSessionFactoryBuilder中的build方法:
public SqlSessionFactory build(Reader reader, String environment, Properties properties) { try { //通过XMLConfigBuilder解析配置文件,解析的配置相关信息都会封装为一个Configuration对象 XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); //这儿创建DefaultSessionFactory对象 而parser.parse()方法返回的就是configuration对象 而configuration对象中存储的就是mybatis配置文件的信息 return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { reader.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } } public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); }
2、当我们创建到SqlSessionFactory之后,就可以通过SqlSessionFactory的openSession方法去获取SqlSession对象。
public interface SqlSessionFactory { //这里可以根据不同的参数获取SqlSession 而在例子中使用的是第一种 SqlSession openSession(); SqlSession openSession(boolean autoCommit); SqlSession openSession(Connection connection); SqlSession openSession(TransactionIsolationLevel level); SqlSession openSession(ExecutorType execType); SqlSession openSession(ExecutorType execType, boolean autoCommit); SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level); SqlSession openSession(ExecutorType execType, Connection connection); Configuration getConfiguration(); } //然后实现方式选DefaultSqlSessionFactory,因为当初我们创建的也是DefaultSqlSessionFactory。 public SqlSession openSession() { return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false); } //通常一系列openSession方法最终都会调用本方法 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { //通过Confuguration对象去获取Mybatis相关配置信息, Environment对象包含了数据源和事务的配置 final Environment environment = configuration.getEnvironment(); final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); //之前说了,从表面上来看,咱们是用sqlSession在执行sql语句, 实际呢,其实是通过excutor执行, excutor是对于Statement的封装 final Executor executor = configuration.newExecutor(tx, execType); //创建了一个DefaultSqlSession对象(关键) return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { closeTransaction(tx); // may have fetched a connection so lets call close() throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
3、当拿到SqlSession之后,可以调用SqlSession中一系列的select…, insert…, update…, delete…方法轻松自如的进行CRUD操作。但在例子中,我们是用dao+xml映射文件的方式集中管理sql,所以要用到mybatis的MapperProxy,动态代理我们自己的dao。
public void findStudentById() { SqlSessionFactory sqlSessionFactory = getSessionFactory(); SqlSession sqlSession = sqlSessionFactory.openSession(); //通过sqlSession中的getMapper方法通过传入一个Class 获取对应的dao StudentDao studentDao = sqlSession.getMapper(StudentDao.class); Student student = studentDao.findStudentById("20140101"); List<Course> courseList = student.getCourseList(); for (Course course: courseList) { System.out.println(course.getId() + " " + course.getName()); } } //直接去configuration中找 public <T> T getMapper(Class<T> type) { return configuration.<T>getMapper(type, this); } //configuration扔给了mapperRegistry去处理 public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); } @SuppressWarnings("unchecked") public <T> T getMapper(Class<T> type, SqlSession sqlSession) { //mapperRegistry交给MapperProxyFactory去处理 final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); if (mapperProxyFactory == null) throw new BindingException("Type " + type + " is not known to the MapperRegistry."); try { //关键方法 return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); } } @SuppressWarnings("unchecked") protected T newInstance(MapperProxy<T> mapperProxy) { //动态代理我们写的dao接口 return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); }
4、通过MapperProxy动态代理dao, 也就是说, 当执行自己写的dao里面的方法的时候,其实是对应的mapperProxy在代理,MapperProxy是怎么做的?
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (Object.class.equals(method.getDeclaringClass())) { try { return method.invoke(this, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } final MapperMethod mapperMethod = cachedMapperMethod(method); //主要交给MapperMethod自己去管 return mapperMethod.execute(sqlSession, args); } //先判断CRUD类型,然后根据类型去选择到底执行sqlSession中的哪个方法 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 { 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; } //回到SqlSession 选择了selectList来分析: public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { try { MappedStatement ms = configuration.getMappedStatement(statement); //CRUD实际上是交给Excetor去处理 List<E> result = executor.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(); } } // Excetor的query方法 这里我们选择BeaseExcetor: public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { BoundSql boundSql = ms.getBoundSql(parameter); CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql); //执行查询方法 return query(ms, parameter, rowBounds, resultHandler, key, boundSql); } 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) throw new ExecutorException("Executor was closed."); if (queryStack == 0 && ms.isFlushCacheRequired()) { 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(); } deferredLoads.clear(); // issue #601 if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { clearLocalCache(); // issue #482 } } return list; } //queryFromDatabase方法: 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 而这个方法就是关键 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; } //这里我们随便选择一个 例: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(); StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); stmt = prepareStatement(handler, ms.getStatementLog()); //StatementHandler封装了Statement, 让 StatementHandler 去处理 return handler.<E>query(stmt, resultHandler); } finally { closeStatement(stmt); } } //看看StatementHandler 的一个实现类 PreparedStatementHandler(这也是我们最常用的,封装的是PreparedStatement) public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); //结果交给了ResultSetHandler 去处理 return resultSetHandler.<E> handleResultSets(ps); }
相关文章推荐
- mybatis源码学习之执行过程分析(1)——SqlSessionFactory及SqlSession的创建
- MyBatis源码分析-SQL语句执行的完整流程
- MyBatis源码分析-SQL语句执行的完整流程
- MyBatis源码分析-SQL语句执行的完整流程
- mybatis源码分析,sql语句执行的完整流程
- Mybatis 源码 sql执行流程分析
- mybatis源码学习之执行过程分析(4)——映射文件中sql的获取和sql语句的执行
- mybatis源码学习之执行过程分析(5)——sql执行后ResultSet的处理及结果返回
- 深入浅出Mybatis系列(十)---SQL执行流程分析(源码篇)(转)
- 深入浅出Mybatis系列(十)---SQL执行流程分析(源码篇)
- 深入浅出Mybatis系列(十)---SQL执行流程分析(源码篇)
- 深入浅出Mybatis系列(十)---SQL执行流程分析(源码篇)
- mybatis(十)SQL执行流程分析(源码篇)
- 源码的角度理解Glide的执行流程 学习笔记
- Mybatis3源码分析(12)-Sql解析执行-MetaObject
- 深入浅出Mybatis系列(十)---SQL执行流程分析(源码篇)
- mybatis源码学习之执行过程分析(2)——config.xml配置文件和mapper.xml映射文件解析过程
- 深入浅出Mybatis系列(十)---SQL执行流程分析(源码篇)
- quartz 2.2.x 源码学习 基本执行流程分析
- mybatis源码学习之执行过程分析(3)——mapper接口的获取