美文网首页Java架构技术栈
长文警告!springboot mybatis 源码

长文警告!springboot mybatis 源码

作者: 若丨寒 | 来源:发表于2020-11-06 16:31 被阅读0次

    springboot-mybatis 整合了 mybatis 对数据库的访问操,其为我做了很好的封装,开箱即用。只要在项目中引入 maven的依赖,打上 MapperScan 的注解就可以进入我们的业务开发。

    我们从注解 MapperScan 入手查看,可知在这个注解用 @Import 在容器中注册了一个 MapperScannerRegistrar 的bean。该类实现了 ImportBeanDefinitionRegistrar 接口,重写了registerBeanDefinitions 方法:

    **1.**
    
      @Override
      public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 获取打了 MapperScan 注解的类上的注解信息,包括 basePackages 等信息,这里即使dao包里的接口没有打 Component 注解也会被注入到容器中。
        AnnotationAttributes mapperScanAttrs = AnnotationAttributes
            .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
        if (mapperScanAttrs != null) {
          registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
              generateBaseBeanName(importingClassMetadata, 0));
        }
      }
    

    获取到注解信息之后,进入其自身的 registerBeanDefinitions 方法:

    **2.**
      void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
          BeanDefinitionRegistry registry, String beanName) {
        // new 了一个 BeanDefinitionBuilder 对象并将其 beanClass 设置为 MapperScannerConfigurer.class。
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
        builder.addPropertyValue("processPropertyPlaceHolders", true);
    
        // 下面都是一些属性的设置
        Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
        if (!Annotation.class.equals(annotationClass)) {
          builder.addPropertyValue("annotationClass", annotationClass);
        }
    
        Class<?> markerInterface = annoAttrs.getClass("markerInterface");
        if (!Class.class.equals(markerInterface)) {
          builder.addPropertyValue("markerInterface", markerInterface);
        }
    
        Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
        if (!BeanNameGenerator.class.equals(generatorClass)) {
          builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass));
        }
    
        Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
        if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
          builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);
        }
    
        String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");
        if (StringUtils.hasText(sqlSessionTemplateRef)) {
          builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));
        }
    
        String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef");
        if (StringUtils.hasText(sqlSessionFactoryRef)) {
          builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));
        }
    
        List<String> basePackages = new ArrayList<>();
        basePackages.addAll(
            Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));
    
        basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText)
            .collect(Collectors.toList()));
    
        basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName)
            .collect(Collectors.toList()));
    
        if (basePackages.isEmpty()) {
          basePackages.add(getDefaultBasePackage(annoMeta));
        }
    
        String lazyInitialization = annoAttrs.getString("lazyInitialization");
        if (StringUtils.hasText(lazyInitialization)) {
          builder.addPropertyValue("lazyInitialization", lazyInitialization);
        }
        // 在builder 中设置 basePackage 属性
        builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
        // 向容器中注册了一个 MapperScannerConfigurer 的bean。
        registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
    
      }
    

    通过上面的代码逻辑我们可知,MapperScannerRegistrar 向容器中注册了一个 MapperScannerConfigurer 的bean。而该类实现了 BeanDefinitionRegistryPostProcessor 接口,查看其 postProcessBeanDefinitionRegistry 方法,在该方法中实现了对 basePackages 包下的 dao 接口文件的解析包装成 bean。

    **3.**
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        if (this.processPropertyPlaceHolders) {
          processPropertyPlaceHolders();
        }
    
        // 创建了一个 ClassPathMapperScanner 用于解析 mapper.xml 文件,下面是一些属性设置。
        ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
        scanner.setAddToConfig(this.addToConfig);
        scanner.setAnnotationClass(this.annotationClass);
        scanner.setMarkerInterface(this.markerInterface);
        scanner.setSqlSessionFactory(this.sqlSessionFactory);
        scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
        scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
        scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
        scanner.setResourceLoader(this.applicationContext);
        scanner.setBeanNameGenerator(this.nameGenerator);
        scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
        if (StringUtils.hasText(lazyInitialization)) {
          scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
        }
        
        // scanner 里面设置过滤器,添加默认的 includeFilters 和 excludeFilters
        scanner.registerFilters();
        // 这里是真正的解析 mapper.xml 文件,生成各个 MapperedStatement 对象保存至 Configuration 中。
        scanner.scan(
            StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
      }
    

    跟进 scanner 的 scan 方法到 ClassPathMapperScanner 的doScan 方法:

    **4.**
    public Set<BeanDefinitionHolder> doScan(String... basePackages) {
      // 调用父类的 doScan 方法,父类是 spring 的类,和 mybatis 没有关系,返回的是一个类型为 BeanDefinition 的 set。
      Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
      if (beanDefinitions.isEmpty()) {
        LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)
            + "' package. Please check your configuration.");
      } else {
      // 这里为beanDefinition 设置属性相关的信息。(**5.**)
        processBeanDefinitions(beanDefinitions);
      }
      return beanDefinitions;
    }
    
    **5.**
    
      private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
        GenericBeanDefinition definition;
        for (BeanDefinitionHolder holder : beanDefinitions) {
          definition = (GenericBeanDefinition) holder.getBeanDefinition();
          String beanClassName = definition.getBeanClassName();
          LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
              + "' mapperInterface");
          definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); 
          // 这里设置 beanClass 为 MapperFactoryBean,之后实例化调用的是这个类的构造器。这个很关键-。-
          definition.setBeanClass(this.mapperFactoryBeanClass);
    
          definition.getPropertyValues().add("addToConfig", this.addToConfig);
    
          boolean explicitFactoryUsed = false;
          if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
            definition.getPropertyValues().add("sqlSessionFactory",
                new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
            explicitFactoryUsed = true;
          } else if (this.sqlSessionFactory != null) {
            definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
            explicitFactoryUsed = true;
          }
    
          if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
            if (explicitFactoryUsed) {
              LOGGER.warn(
                  () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
            }
            definition.getPropertyValues().add("sqlSessionTemplate",
                new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
            explicitFactoryUsed = true;
          } else if (this.sqlSessionTemplate != null) {
            if (explicitFactoryUsed) {
              LOGGER.warn(
                  () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
            }
            definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
            explicitFactoryUsed = true;
          }
    
          if (!explicitFactoryUsed) {
            LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
            definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
          }
          definition.setLazyInit(lazyInitialization);
        }
      }
    

    到这里的 beanDefinition 的创建及属性设置基本完毕,接下来是 bean 的创建。spring bean 实例化之后,调用 populateBean 方法设置属性,其中有个autowireByType​ 方法 对属性重新获取,并通过 setter 方法,将 sqlSessionTemplate​ 作为属性注入。而这个 sqlSessionTemplate 包含了 mapper.xml 文件的解析结果,后期需要执行的 sql 都在这里。接下来我们需要知道这个 sqlSessionTemplate bean 是如何被创建的。

    我们查看 mybatis 的自动配置类 MybatisAutoConfiguration 其的两个方法:sqlSessionFactory 和 sqlSessionTemplate 分别向容器中注入了 sqlSessionFactory 和 SqlSessionTemplate 的 bean。提供了我们自定义的dao 中这两个属性的注入。我们先看sqlSessionFactory 方法,SqlSessionTemplate 方法只不过是将 sqlSessionFactory 作为参数传入并 new 了一个 SqlSessionTemplate 的 bean。

    **6.**
      public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
        factory.setDataSource(dataSource);
        factory.setVfs(SpringBootVFS.class);
        if (StringUtils.hasText(this.properties.getConfigLocation())) {
          factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
        }
        // 这里其实是新建了一个 Configuration 对象,后面的代码都是一些属性的设置。
        applyConfiguration(factory);
        if (this.properties.getConfigurationProperties() != null) {
          factory.setConfigurationProperties(this.properties.getConfigurationProperties());
        }
        if (!ObjectUtils.isEmpty(this.interceptors)) {
          factory.setPlugins(this.interceptors);
        }
        if (this.databaseIdProvider != null) {
          factory.setDatabaseIdProvider(this.databaseIdProvider);
        }
        if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
          factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
        }
        if (this.properties.getTypeAliasesSuperType() != null) {
          factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());
        }
        if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
          factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
        }
        if (!ObjectUtils.isEmpty(this.typeHandlers)) {
          factory.setTypeHandlers(this.typeHandlers);
        }
        if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
          // 设置 mapper.xml 文件的位置。
          factory.setMapperLocations(this.properties.resolveMapperLocations());
        }
        Set<String> factoryPropertyNames = Stream
            .of(new BeanWrapperImpl(SqlSessionFactoryBean.class).getPropertyDescriptors()).map(PropertyDescriptor::getName)
            .collect(Collectors.toSet());
        Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();
        if (factoryPropertyNames.contains("scriptingLanguageDrivers") && !ObjectUtils.isEmpty(this.languageDrivers)) {
          // Need to mybatis-spring 2.0.2+
          factory.setScriptingLanguageDrivers(this.languageDrivers);
          if (defaultLanguageDriver == null && this.languageDrivers.length == 1) {
            defaultLanguageDriver = this.languageDrivers[0].getClass();
          }
        }
        if (factoryPropertyNames.contains("defaultScriptingLanguageDriver")) {
          // Need to mybatis-spring 2.0.2+
          factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver);
        }
        // 关键方法,进入:**7**
        return factory.getObject();
      }
    

    我们跟进步骤6中的 factory.getObject() 方法会进入到 SqlSessionFactory 的 buildSqlSessionFactory 方法:

    **7.**
    
      protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
    
        final Configuration targetConfiguration;
        // 前面的操作都是一些属性的设置
        XMLConfigBuilder xmlConfigBuilder = null;
        if (this.configuration != null) {
          targetConfiguration = this.configuration;
          if (targetConfiguration.getVariables() == null) {
            targetConfiguration.setVariables(this.configurationProperties);
          } else if (this.configurationProperties != null) {
            targetConfiguration.getVariables().putAll(this.configurationProperties);
          }
        } else if (this.configLocation != null) {
          xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
          targetConfiguration = xmlConfigBuilder.getConfiguration();
        } else {
          LOGGER.debug(
              () -> "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
          targetConfiguration = new Configuration();
          Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);
        }
    
        Optional.ofNullable(this.objectFactory).ifPresent(targetConfiguration::setObjectFactory);
        Optional.ofNullable(this.objectWrapperFactory).ifPresent(targetConfiguration::setObjectWrapperFactory);
        Optional.ofNullable(this.vfs).ifPresent(targetConfiguration::setVfsImpl);
    
        if (hasLength(this.typeAliasesPackage)) {
          scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream()
              .filter(clazz -> !clazz.isAnonymousClass()).filter(clazz -> !clazz.isInterface())
              .filter(clazz -> !clazz.isMemberClass()).forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias);
        }
    
        if (!isEmpty(this.typeAliases)) {
          Stream.of(this.typeAliases).forEach(typeAlias -> {
            targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias);
            LOGGER.debug(() -> "Registered type alias: '" + typeAlias + "'");
          });
        }
    
        if (!isEmpty(this.plugins)) {
          Stream.of(this.plugins).forEach(plugin -> {
            targetConfiguration.addInterceptor(plugin);
            LOGGER.debug(() -> "Registered plugin: '" + plugin + "'");
          });
        }
    
        if (hasLength(this.typeHandlersPackage)) {
          scanClasses(this.typeHandlersPackage, TypeHandler.class).stream().filter(clazz -> !clazz.isAnonymousClass())
              .filter(clazz -> !clazz.isInterface()).filter(clazz -> !Modifier.isAbstract(clazz.getModifiers()))
              .forEach(targetConfiguration.getTypeHandlerRegistry()::register);
        }
    
        if (!isEmpty(this.typeHandlers)) {
          Stream.of(this.typeHandlers).forEach(typeHandler -> {
            targetConfiguration.getTypeHandlerRegistry().register(typeHandler);
            LOGGER.debug(() -> "Registered type handler: '" + typeHandler + "'");
          });
        }
    
        targetConfiguration.setDefaultEnumTypeHandler(defaultEnumTypeHandler);
    
        if (!isEmpty(this.scriptingLanguageDrivers)) {
          Stream.of(this.scriptingLanguageDrivers).forEach(languageDriver -> {
            targetConfiguration.getLanguageRegistry().register(languageDriver);
            LOGGER.debug(() -> "Registered scripting language driver: '" + languageDriver + "'");
          });
        }
        Optional.ofNullable(this.defaultScriptingLanguageDriver)
            .ifPresent(targetConfiguration::setDefaultScriptingLanguage);
    
        if (this.databaseIdProvider != null) {// fix #64 set databaseId before parse mapper xmls
          try {
            targetConfiguration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
          } catch (SQLException e) {
            throw new NestedIOException("Failed getting a databaseId", e);
          }
        }
    
        Optional.ofNullable(this.cache).ifPresent(targetConfiguration::addCache);
    
        if (xmlConfigBuilder != null) {
          try {
            xmlConfigBuilder.parse();
            LOGGER.debug(() -> "Parsed configuration file: '" + this.configLocation + "'");
          } catch (Exception ex) {
            throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
          } finally {
            ErrorContext.instance().reset();
          }
        }
    
        targetConfiguration.setEnvironment(new Environment(this.environment,
            this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
            this.dataSource));
    
        if (this.mapperLocations != null) {
          if (this.mapperLocations.length == 0) {
            LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
          } else {
          
          // 从这里开始解析 mapper.xml 文件
            for (Resource mapperLocation : this.mapperLocations) {
              if (mapperLocation == null) {
                continue;
              }
              try {
                XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
                    targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
                // XMLMapperBuilder 解析,跟进之 **8.**
                xmlMapperBuilder.parse();
              } catch (Exception e) {
                throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
              } finally {
                ErrorContext.instance().reset();
              }
              LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");
            }
          }
        } else {
          LOGGER.debug(() -> "Property 'mapperLocations' was not specified.");
        }
    
        return this.sqlSessionFactoryBuilder.build(targetConfiguration);
      }
    

    我们继续跟进 XMLMapperBuilder 的 parse 方法:

    **8.**
    
      public void parse() {
        if (!configuration.isResourceLoaded(resource)) {
          // 从mapper 文件的根节点开始解析(**9.**)
          configurationElement(parser.evalNode("/mapper"));
          configuration.addLoadedResource(resource);
          bindMapperForNamespace();
        }
    
        parsePendingResultMaps();
        parsePendingCacheRefs();
        parsePendingStatements();
      }
    

    查看 configurationElement 方法:

    **9.**
      private void configurationElement(XNode context) {
        try {
          String namespace = context.getStringAttribute("namespace");
          if (namespace == null || namespace.equals("")) {
            throw new BuilderException("Mapper's namespace cannot be empty");
          }
          // 此处开始根据 xml 的各个节点解析,并将结果存储到 Configuration 对象中,ResultMap节点解析并存储在 resultMaps 中,增删改查的 sql 封装成一个个 MappedStatement 对象中。
          builderAssistant.setCurrentNamespace(namespace);
          cacheRefElement(context.evalNode("cache-ref"));
          cacheElement(context.evalNode("cache"));
          parameterMapElement(context.evalNodes("/mapper/parameterMap"));
          resultMapElements(context.evalNodes("/mapper/resultMap"));
          sqlElement(context.evalNodes("/mapper/sql"));
          buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
        } catch (Exception e) {
          throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);测
        }
      }
    

    到此为止,mybatis 对于配置文件的解析,封装 sql 的工作基本做好了,接下来的工作是如何调用。

    当我们调用我们写好的 dao 上的业务方法时,最外面的一层是 jdk 的动态代理,接着往里面一层是 mybaits 的 MapperProxy 代理。查看 MapperProxy 的 invoke 方法:

    **10.**
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
          if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
          } else if (method.isDefault()) {
            if (privateLookupInMethod == null) {
              return invokeDefaultMethodJava8(proxy, method, args);
            } else {
              return invokeDefaultMethodJava9(proxy, method, args);
            }
          }
        } catch (Throwable t) {
          throw ExceptionUtil.unwrapThrowable(t);
        }
        
        // 上面的方法略过,直接看下面的 execute 方法,这里的 MapperMethod 做了一层缓存
        final MapperMethod mapperMethod = cachedMapperMethod(method);
        // 正真执行 sql 。跟进 MapperMethod 的 execute 方法(**11.**)
        return mapperMethod.execute(sqlSession, args);
      }
    
    **11.**
      public Object execute(SqlSession sqlSession, Object[] args) {
        Object result;
        // 判断 sql 的类型,增删改查。
        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 {
              Object param = method.convertArgsToSqlCommandParam(args);
              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;
      }
    

    步骤 11 中根据 sql 的类型来判断执行什么流程,以下以 insert 为例:

    **12.**
      public int insert(String statement, Object parameter) {
        **11.**中的 sqlSession 为 SqlSessionTemplate,这里的 qlSessionProxy 又是一层代理,代理了 DefaultSqlSession 的 update 方法。
        return this.sqlSessionProxy.insert(statement, parameter);
      }
    
    **13.**
    public int update(String statement, Object parameter) {
        try {
          dirty = true;
          // 从 Configuration 对象中获取 MappedStatement 对象。
          MappedStatement ms = configuration.getMappedStatement(statement);
          // sql 的是 executor。
          return executor.update(ms, wrapCollection(parameter));
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }
    

    继续跟进进入到了 SimpleExecutor 的 doUpdate 方法:

    **14.**
      public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
        Statement stmt = null;
        try {
          Configuration configuration = ms.getConfiguration();
          StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
          // 连接数据库创建 prepareStatement,并使用 handler 为 sql 设置参数。
          stmt = prepareStatement(handler, ms.getStatementLog());
          // 执行封装好的 sql。
          return handler.update(stmt);
        } finally {
          closeStatement(stmt);
        }
      }
    

    在 SimpleExecutor 中,对于增删改的操作执行的是 doUpdate 方法,而查询走的是 doQuery 方法。

    **15.**
      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());
          // 这里的 resultHandler 对数据库查询结果做了封装,封装成 resultMap 对应的 java 类型,再看下 handler.query 方法(**16.**)
          return handler.query(stmt, resultHandler);
        } finally {
          closeStatement(stmt);
        }
      }
    handler.query 会进入到 PreparedStatementHandler 的 query 方法里:
    
    **16.**
      public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        // preparedStatement 的执行,该逻辑属于 jdbc,不属于 mybatis 的范畴。
        PreparedStatement ps = (PreparedStatement) statement;
        ps.execute();
        // resultHandler 对查询的结果进行封装成我们想要的 java 类型。
        return resultSetHandler.handleResultSets(ps);
      }
    

    小结
    主要从两个方面讲述了 springboot-mybatis 的基本功能:MapperScan 使用 @Import 在容器中注册了一个 MapperScannerRegistrar,实现了对 basePackages 的接口文件的解析,并替换掉 bean 的类型为 MapperFactoryBean。而 MybatisAutoConfiguration 则创建了 SqlSessionFactory 的 bean,创建过程中完成了对 mapper.xml 文件的解析,并存放于 SqlSessionFactory 的 Confuguration 属性当中。而在自定义 dao 的 bean 实例化之后,通过属性注入,将 SqlSessionFactory 注入到各个 dao 的 bean 中。在 dao 方法的执行中, jdk 代理的是 MapperProxy,实际通过执行 MapperMethod 方法。sql 语句的执行由 Executor 执行,在获取数据的的 preparedStatment 之后,通过 ParameterHandler 对预编译语句参数设置。ResultHandler 对数据查询结果封装成我们需要的 java 类型。

    来源:https://www.tuicool.com/articles/J7ZzauE

    相关文章

      网友评论

        本文标题:长文警告!springboot mybatis 源码

        本文链接:https://www.haomeiwen.com/subject/swwtbktx.html