前言

MyBatis主要是在做两件事,一件是解析xml文件,一件就是执行SQL了。在上篇我们已经解析了mybatis-config.xml配置文件和Mapper.xml映射文件,本篇我们看是如何执行一条SQL的。

SQL执行

看MyBatis如何调用一个方法来执行SQL的

创建SqlSession

创建一个会话

  1. SqlSession sqlSession = sqlSessionFactory.openSession()

在MyBatis的单元测试AutoConstructorTest.fullyPopulatedSubject()中 从sqlSessionFactory创建一个SqlSession对象,默认是使用DefaultSqlSession。创建的同时,我们从Configuration这个对象中获取Executor执行类型,默认SIMPLE。

  1. // DefaultSqlSessionFactory 类
  2. private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
  3. Transaction tx = null;
  4. try {
  5. // 环境数据源配置
  6. final Environment environment = configuration.getEnvironment();
  7. final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
  8. tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
  9. // 创建一个简单执行器
  10. final Executor executor = configuration.newExecutor(tx, execType);
  11. // 创建一个默认的SqlSession
  12. return new DefaultSqlSession(configuration, executor, autoCommit);
  13. } catch (Exception e) {
  14. closeTransaction(tx);
  15. throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
  16. } finally {
  17. // 清空该线程上下文信息
  18. ErrorContext.instance().reset();
  19. }
  20. }

获取Mapper对象

  1. // 获取Mapper对象
  2. AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);

AutoConstructorMapper是一个接口不能直接调用方法,MyBatis利用JDK的动态代理为我们创建了一个对象

  1. /**
  2. * MapperRegistry 类
  3. * @param type 类名,例如:org.apache.ibatis.autoconstructor.AutoConstructorMapper
  4. * @param sqlSession
  5. * @return 返回type的一个对象
  6. */
  7. public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  8. // knownMappers 是个Map,key是类名,value是MapperProxyFactory代理工厂
  9. // 在解析配置文件的时候会添加进去的
  10. final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
  11. if (mapperProxyFactory == null) {
  12. throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
  13. }
  14. try {
  15. // 通过mapperProxy new一个对象
  16. return mapperProxyFactory.newInstance(sqlSession);
  17. } catch (Exception e) {
  18. throw new BindingException("Error getting mapper instance. Cause: " + e, e);
  19. }
  20. }
  1. // MapperProxyFactory 类
  2. protected T newInstance(MapperProxy<T> mapperProxy) {
  3. return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  4. }

执行SQL

通过JDK动态代理创建了一个代理对象,这时候我们调用findById()方法,会执行MapperProxy类中的invoke()方法,因为该类实现了InvocationHandler接口

  1. /**
  2. *
  3. * @param proxy
  4. * @param method 执行的方法,例如:findById(int)
  5. * @param args 该方法的入参,例如:1
  6. * @return
  7. * @throws Throwable
  8. */
  9. @Override
  10. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  11. try {
  12. if (Object.class.equals(method.getDeclaringClass())) {
  13. return method.invoke(this, args);
  14. } else if (method.isDefault()) {
  15. return invokeDefaultMethod(proxy, method, args);
  16. }
  17. } catch (Throwable t) {
  18. throw ExceptionUtil.unwrapThrowable(t);
  19. }
  20. final MapperMethod mapperMethod = cachedMapperMethod(method);
  21. // 去执行sql
  22. return mapperMethod.execute(sqlSession, args);
  23. }

来到MapperMethod类中的execute方法

  1. public Object execute(SqlSession sqlSession, Object[] args) {
  2. case INSERT: {
  3. // insert 代码逻辑
  4. }
  5. case UPDATE: {
  6. // update 代码逻辑
  7. }
  8. case DELETE: {
  9. // delete 代码逻辑
  10. }
  11. case SELECT: {
  12. // 省略其它一些判断
  13. // 参数
  14. Object param = method.convertArgsToSqlCommandParam(args);
  15. // command.getName()其实就是完整的方法名称,例如:
  16. // org.apache.ibatis.autoconstructor.AutoConstructorMapper.findById
  17. // 执行DefaultSqlSession类下的selectOne()方法
  18. result = sqlSession.selectOne(command.getName(), param);
  19. if (method.returnsOptional()
  20. && (result == null || !method.getReturnType().equals(result.getClass()))) {
  21. result = Optional.ofNullable(result);
  22. }
  23. }
  24. }

通过DefaultSqlSession类中的多个构造方法,最终来到该方法查询

  1. // DefaultSqlSession 类
  2. @Override
  3. public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
  4. try {
  5. // MappedStatement对象有要执行的sql,例如:SELECT * FROM subject WHERE id = ?
  6. MappedStatement ms = configuration.getMappedStatement(statement);
  7. // 调用核心组件Executor,它有很多的实现类,进行执行
  8. return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
  9. } catch (Exception e) {
  10. throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
  11. } finally {
  12. ErrorContext.instance().reset();
  13. }
  14. }

statement包下的*StatementHandler类进行和JDBC的操作

  1. // PreparedStatementHandler 类
  2. @Override
  3. public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  4. // 这里就是我们的JDBC PreparedStatement对象了
  5. PreparedStatement ps = (PreparedStatement) statement;
  6. // 执行sql
  7. ps.execute();
  8. // ResultSetHandler接口 对查询结果集进行封装处理
  9. return resultSetHandler.handleResultSets(ps);
  10. }

查询结果集处理

ResultSetHandler的实现类DefaultResultSetHandler进行结果及处理,具体处理在这个核心方法

  1. @Override
  2. public List<Object> handleResultSets(Statement stmt) throws SQLException {
  3. ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
  4. final List<Object> multipleResults = new ArrayList<>();
  5. int resultSetCount = 0;
  6. // 获取第一个结果集
  7. // 在这里我们查询的一个对象,ResultSetWrapper有该对象的属性,属性类型,对应的JDBC类型
  8. // 还有对应数据库查询结果
  9. ResultSetWrapper rsw = getFirstResultSet(stmt);
  10. List<ResultMap> resultMaps = mappedStatement.getResultMaps();
  11. int resultMapCount = resultMaps.size();
  12. validateResultMapsCount(rsw, resultMapCount);
  13. while (rsw != null && resultMapCount > resultSetCount) {
  14. ResultMap resultMap = resultMaps.get(resultSetCount);
  15. // 数据库字段和Java类属性之间的映射处理
  16. // 同时将这个对象添加到multipleResults集合中
  17. handleResultSet(rsw, resultMap, multipleResults, null);
  18. rsw = getNextResultSet(stmt);
  19. cleanUpAfterHandlingResultSet();
  20. resultSetCount++;
  21. }
  22. String[] resultSets = mappedStatement.getResultSets();
  23. if (resultSets != null) {
  24. while (rsw != null && resultSetCount < resultSets.length) {
  25. ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
  26. if (parentMapping != null) {
  27. String nestedResultMapId = parentMapping.getNestedResultMapId();
  28. ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
  29. handleResultSet(rsw, resultMap, null, parentMapping);
  30. }
  31. rsw = getNextResultSet(stmt);
  32. cleanUpAfterHandlingResultSet();
  33. resultSetCount++;
  34. }
  35. }
  36. // 调用#1,如果集合只有一个元素,就取下标为0的元素返回,否则就返回所有
  37. return collapseSingleResultList(multipleResults);
  38. }
  39. // #1
  40. private List<Object> collapseSingleResultList(List<Object> multipleResults) {
  41. return multipleResults.size() == 1
  42. ? (List<Object>) multipleResults.get(0)
  43. : multipleResults;
  44. }

小结

本篇我们对mapper调用方法到执行具体的SQL,很粗略的走了一遍,会经过MapperProxy,DefaultSqlSession,MappedStatement,Executor,ResultHandler这几个重要的类。了解了MyBatis是如何通过调用方法来执行SQL的,其中具体的处理细节感兴趣,可以一步步Debug,多Debug几遍,一探究竟!

请你相信我所说的都是错的