亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

MyBatis中的@SelectProvider注解源碼分析

 更新時(shí)間:2024年01月25日 09:34:31   作者:葉長(zhǎng)風(fēng)  
這篇文章主要介紹了MyBatis中的@SelectProvider注解源碼分析,@SelectProvider功能就是用來(lái)單獨(dú)寫(xiě)一個(gè)class類(lèi)與方法,用來(lái)提供一些xml或者注解中不好寫(xiě)的sql,今天就來(lái)說(shuō)下這個(gè)注解的具體用法與源碼,需要的朋友可以參考下

@SelectProvider注解用法

寫(xiě)一個(gè)簡(jiǎn)單的@SelectProvider的用法,新建class類(lèi),添加一個(gè)根據(jù)userId查詢(xún)user的方法。

SelectSqlProvider

public class SelectSqlProvider {
    public String selectByUserId(Long id) {
        StringBuffer buffer = new StringBuffer();
        buffer.append("SELECT * FROM user where ");
        buffer.append("id = ").append(id).append(";");
        return buffer.toString();
    }
}

SelectSqlProvider中提供了一個(gè)很簡(jiǎn)單的查詢(xún)方法,根據(jù)userId返回user對(duì)象,里面就是用了一個(gè)StringBuffer對(duì)象來(lái)拼接一個(gè)SQL語(yǔ)句,我想更多的是想用MyBatis中的SQL Builder的寫(xiě)法,SQL Builder寫(xiě)法在官方網(wǎng)站地址為//www.mybatis.org/mybatis-3/zh/statement-builders.html,不得不說(shuō)SQL Builder的寫(xiě)法確實(shí)比較漂亮,很工整,不過(guò)也是看自己運(yùn)用的熟練程度吧。

UserMapper

@ResultMap("BaseResultMap")
@SelectProvider(type = SelectSqlProvider.class, method = "selectByUserId")
User getUserByUserId(long id);

mapper中的其他方法就不貼出來(lái)了,需要說(shuō)的就是這一個(gè),這一個(gè)方法在xml中沒(méi)有對(duì)應(yīng)的sql,在該方法上也沒(méi)有@Select注解修飾,只有@SelectProvider注解,@SelectProvider中兩個(gè)屬性,type為提供sql的class類(lèi),method為指定方法。

對(duì)應(yīng)Mapper的調(diào)用與結(jié)果在這就不再分析了,就是簡(jiǎn)單的返回user對(duì)象,下文將是對(duì)@SelectProvider注解作用的詳解。

@SelectProvider源碼分析

說(shuō)起Select查詢(xún),基本就又是回到我們先前那幾篇文章說(shuō)的了,@SelectProvider注解加載問(wèn)題,之前的文章中說(shuō)了如何在解析xml之后解析注解中的SQL,這一種無(wú)非換了種樣式,從由注解提供改為了從class類(lèi)中單獨(dú)寫(xiě)方法提供SQL,我們來(lái)看下相關(guān)源碼實(shí)現(xiàn)。

這里就還要回到mapper的解析處,回到開(kāi)始的parseConfiguration方法中mapperElement。

mapperElement(root.evalNode("mappers"));

這一行在解析xml文件之后,最后進(jìn)行了addMapper操作。

private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          String mapperPackage = child.getStringAttribute("name");
          configuration.addMappers(mapperPackage);
        } else {
          String resource = child.getStringAttribute("resource");
          String url = child.getStringAttribute("url");
          String mapperClass = child.getStringAttribute("class");
          if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url != null && mapperClass == null) {
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url == null && mapperClass != null) {
            Class<?> mapperInterface = Resources.classForName(mapperClass);
            configuration.addMapper(mapperInterface);
          } else {
            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
          }
        }
      }
    }
  }

但是從前文中我們知addMapper操作不僅將mapper保存進(jìn)knownMappers中,并且還進(jìn)行了注解Mapper的解析,從而實(shí)現(xiàn)了對(duì)注解sql的加載,同時(shí)**@SelectProvider**也是在這里進(jìn)行加載的。

knownMappers.put(type, new MapperProxyFactory<T>(type));
        // It's important that the type is added before the parser is run
        // otherwise the binding may automatically be attempted by the
        // mapper parser. If the type is already known, it won't try.
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;

進(jìn)入到parse方法中,parse方法最終轉(zhuǎn)到parseStatement方法,在parseStatement方法中,在獲取SqlSource對(duì)象時(shí),對(duì)method方法進(jìn)行了進(jìn)一步的解析。

SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
  private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver) {
    try {
      Class<? extends Annotation> sqlAnnotationType = getSqlAnnotationType(method);
      Class<? extends Annotation> sqlProviderAnnotationType = getSqlProviderAnnotationType(method);
      if (sqlAnnotationType != null) {
        if (sqlProviderAnnotationType != null) {
          throw new BindingException("You cannot supply both a static SQL and SqlProvider to method named " + method.getName());
        }
        Annotation sqlAnnotation = method.getAnnotation(sqlAnnotationType);
        final String[] strings = (String[]) sqlAnnotation.getClass().getMethod("value").invoke(sqlAnnotation);
        return buildSqlSourceFromStrings(strings, parameterType, languageDriver);
      } else if (sqlProviderAnnotationType != null) {
        Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType);
        return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation, type, method);
      }
      return null;
    } catch (Exception e) {
      throw new BuilderException("Could not find value method on SQL annotation.  Cause: " + e, e);
    }
  }

這里可以加上斷點(diǎn),對(duì)我們上面寫(xiě)的代碼調(diào)試一下

到這一步就是對(duì)@SelectProvider注解的解析,可以看到此時(shí)的method方法為getUserByUserId。type類(lèi)型為UserMapper等等。我們繼續(xù)進(jìn)入到ProviderSqlSource中,看看是如何組裝sql的。

public ProviderSqlSource(Configuration configuration, Object provider, Class<?> mapperType, Method mapperMethod) {
    String providerMethodName;
    try {
      this.configuration = configuration;
      this.sqlSourceParser = new SqlSourceBuilder(configuration);
      this.providerType = (Class<?>) provider.getClass().getMethod("type").invoke(provider);
      providerMethodName = (String) provider.getClass().getMethod("method").invoke(provider);
      for (Method m : this.providerType.getMethods()) {
        if (providerMethodName.equals(m.getName()) && CharSequence.class.isAssignableFrom(m.getReturnType())) {
          if (providerMethod != null){
            throw new BuilderException("Error creating SqlSource for SqlProvider. Method '"
                    + providerMethodName + "' is found multiple in SqlProvider '" + this.providerType.getName()
                    + "'. Sql provider method can not overload.");
          }
          this.providerMethod = m;
          this.providerMethodArgumentNames = new ParamNameResolver(configuration, m).getNames();
          this.providerMethodParameterTypes = m.getParameterTypes();
        }
      }
    } catch (BuilderException e) {
      throw e;
    } catch (Exception e) {
      throw new BuilderException("Error creating SqlSource for SqlProvider.  Cause: " + e, e);
    }
    if (this.providerMethod == null) {
      throw new BuilderException("Error creating SqlSource for SqlProvider. Method '"
          + providerMethodName + "' not found in SqlProvider '" + this.providerType.getName() + "'.");
    }
    for (int i = 0; i< this.providerMethodParameterTypes.length; i++) {
      Class<?> parameterType = this.providerMethodParameterTypes[i];
      if (parameterType == ProviderContext.class) {
        if (this.providerContext != null){
          throw new BuilderException("Error creating SqlSource for SqlProvider. ProviderContext found multiple in SqlProvider method ("
              + this.providerType.getName() + "." + providerMethod.getName()
              + "). ProviderContext can not define multiple in SqlProvider method argument.");
        }
        this.providerContext = new ProviderContext(mapperType, mapperMethod);
        this.providerContextIndex = i;
      }
    }
  }

此處對(duì)sqlSourceParser與providerType、providerMethodName等參數(shù)進(jìn)行了實(shí)例化與賦值,最后返回sqlSource對(duì)象。 此處得到的可以說(shuō)還不是原有的sql,所以在Select查詢(xún)的時(shí)候,還要繼續(xù)追蹤看一下到底是如何執(zhí)行sql的,這就要繼續(xù)回到Select查詢(xún)方法了,在前面很多文章中知最后查詢(xún)調(diào)用基本都是調(diào)用的selectList方法,此處還是要從這里分析開(kāi)始。

@Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

進(jìn)入到executor.query方法中,executor的實(shí)現(xiàn)有兩種,一種是BaseExecutor,一種是CacheingExecutor,而這種的初始化條件為openSession中的newExecutor方法。

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

這里一般就是實(shí)例化為Simple類(lèi)型,但是如果cacheEnable字段為true的話(huà),返回CachingExecutor對(duì)象。

而cacheEnable字段算得上是之前漏說(shuō)了的一個(gè)屬性,這個(gè)是在loadSettings時(shí)進(jìn)行初始化的,而如果沒(méi)有設(shè)置cacheEnable字段時(shí),默認(rèn)設(shè)置為true,如下:

private void settingsElement(Properties props) throws Exception { configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
    configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
    configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")))
  }

在說(shuō)完BaseExecutor和CacheingExecutor之后,此處繼續(xù)回到query方法。

@Override
  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);
 }

在query方法中獲取到boundSql對(duì)象,此處可以調(diào)試一下代碼,看看boundSql中有什么參數(shù)。

此處已經(jīng)完成了sql的組裝,繼續(xù)getBoundSql看看進(jìn)行了什么操作。

public BoundSql getBoundSql(Object parameterObject) {
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings == null || parameterMappings.isEmpty()) {
      boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
    }
    // check for nested result maps in parameter mappings (issue #30)
    for (ParameterMapping pm : boundSql.getParameterMappings()) {
      String rmId = pm.getResultMapId();
      if (rmId != null) {
        ResultMap rm = configuration.getResultMap(rmId);
        if (rm != null) {
          hasNestedResultMaps |= rm.hasNestedResultMaps();
        }
      }
    }
    return boundSql;
  }

還需要繼續(xù)追溯sqlSource.getBoundSql(parameterObject),此處SqlSource毫無(wú)疑問(wèn)為ProviderSqlSource類(lèi)。

@Override
  public BoundSql getBoundSql(Object parameterObject) {
    SqlSource sqlSource = createSqlSource(parameterObject);
    return sqlSource.getBoundSql(parameterObject);
  }
private SqlSource createSqlSource(Object parameterObject) {
    try {
      int bindParameterCount = providerMethodParameterTypes.length - (providerContext == null ? 0 : 1);
      String sql;
      if (providerMethodParameterTypes.length == 0) {
        sql = invokeProviderMethod();
      } else if (bindParameterCount == 0) {
        sql = invokeProviderMethod(providerContext);
      } else if (bindParameterCount == 1 &&
              (parameterObject == null || providerMethodParameterTypes[(providerContextIndex == null || providerContextIndex == 1) ? 0 : 1].isAssignableFrom(parameterObject.getClass()))) {
        sql = invokeProviderMethod(extractProviderMethodArguments(parameterObject));
      } else if (parameterObject instanceof Map) {
        @SuppressWarnings("unchecked")
        Map<String, Object> params = (Map<String, Object>) parameterObject;
        sql = invokeProviderMethod(extractProviderMethodArguments(params, providerMethodArgumentNames));
      } else {
        throw new BuilderException("Error invoking SqlProvider method ("
                + providerType.getName() + "." + providerMethod.getName()
                + "). Cannot invoke a method that holds "
                + (bindParameterCount == 1 ? "named argument(@Param)": "multiple arguments")
                + " using a specifying parameterObject. In this case, please specify a 'java.util.Map' object.");
      }
      Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
      return sqlSourceParser.parse(replacePlaceholder(sql), parameterType, new HashMap<String, Object>());
    } catch (BuilderException e) {
      throw e;
    } catch (Exception e) {
      throw new BuilderException("Error invoking SqlProvider method ("
          + providerType.getName() + "." + providerMethod.getName()
          + ").  Cause: " + e, e);
    }
  }

createSqlSource方法有些意思,對(duì)參數(shù)個(gè)數(shù)進(jìn)行了校驗(yàn),如果是沒(méi)有參數(shù),直接執(zhí)行invokeProviderMethod()方法,如果是一個(gè)則進(jìn)行傳參,如果多個(gè)判斷當(dāng)前類(lèi)型是否是Map類(lèi)型,否則拋錯(cuò),等會(huì)倒是可以測(cè)試一下,這里看下invokeProviderMethod方法。

  private String invokeProviderMethod(Object... args) throws Exception {
    Object targetObject = null;
    if (!Modifier.isStatic(providerMethod.getModifiers())) {
      targetObject = providerType.newInstance();
    }
    CharSequence sql = (CharSequence) providerMethod.invoke(targetObject, args);
    return sql != null ? sql.toString() : null;
  }

invokeProviderMethod方法其實(shí)就沒(méi)多少可說(shuō)的了,對(duì)當(dāng)前方法、對(duì)象進(jìn)行了一個(gè)反射獲取值的操作,從而拿到對(duì)應(yīng)sql。

在獲取到sql之后剩下的執(zhí)行就和常規(guī)的是一樣的了,這里就不再繼續(xù)說(shuō)后面的東西了。

到此這篇關(guān)于MyBatis中的@SelectProvider注解源碼分析的文章就介紹到這了,更多相關(guān)@SelectProvider注解源碼內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解Java中方法重寫(xiě)和方法重載的6個(gè)區(qū)別

    詳解Java中方法重寫(xiě)和方法重載的6個(gè)區(qū)別

    方法重寫(xiě)和方法重載都是面向?qū)ο缶幊讨?,那么方法重?xiě)和方法重載有哪些區(qū)別,本文就詳細(xì)的來(lái)介紹一下,感興趣的可以了解一下
    2022-01-01
  • 在VSCode里使用Jupyter?Notebook調(diào)試Java代碼的詳細(xì)過(guò)程

    在VSCode里使用Jupyter?Notebook調(diào)試Java代碼的詳細(xì)過(guò)程

    Jupyter Notebook是以網(wǎng)頁(yè)的形式打開(kāi),可以在網(wǎng)頁(yè)頁(yè)面中直接編寫(xiě)代碼和運(yùn)行代碼,代碼的運(yùn)行結(jié)果也會(huì)直接在代碼塊下顯示的程序,這篇文章主要介紹了在VSCode里使用Jupyter?Notebook,調(diào)試Java代碼,需要的朋友可以參考下
    2022-07-07
  • spring security在分布式項(xiàng)目下的配置方法(案例詳解)

    spring security在分布式項(xiàng)目下的配置方法(案例詳解)

    這篇文章主要介紹了spring security在分布式項(xiàng)目下的配置方法,本文通過(guò)一個(gè)項(xiàng)目案例給大家詳細(xì)介紹,通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-10-10
  • 通過(guò)Java實(shí)現(xiàn)在Word中創(chuàng)建可填充表單

    通過(guò)Java實(shí)現(xiàn)在Word中創(chuàng)建可填充表單

    這篇文章主要為大家詳細(xì)介紹了如何通過(guò)Java代碼,以編程方式在Word中創(chuàng)建可填充表單,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下
    2023-03-03
  • Maven倉(cāng)庫(kù)無(wú)用文件和文件夾清理的方法實(shí)現(xiàn)

    Maven倉(cāng)庫(kù)無(wú)用文件和文件夾清理的方法實(shí)現(xiàn)

    這篇文章主要介紹了Maven倉(cāng)庫(kù)無(wú)用文件和文件夾清理的方法實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • springboot項(xiàng)目整合注冊(cè)功能模塊開(kāi)發(fā)實(shí)戰(zhàn)

    springboot項(xiàng)目整合注冊(cè)功能模塊開(kāi)發(fā)實(shí)戰(zhàn)

    這篇文章主要介紹了springboot項(xiàng)目整合注冊(cè)功能模塊開(kāi)發(fā)實(shí)戰(zhàn),在用戶(hù)的注冊(cè)是首先需要查詢(xún)當(dāng)前的用戶(hù)名是否存在,如果存在則不能進(jìn)行注冊(cè),相當(dāng)于一個(gè)查詢(xún)語(yǔ)句,本文通過(guò)實(shí)例代碼詳細(xì)講解,需要的朋友可以參考下
    2022-11-11
  • Spring boot進(jìn)行參數(shù)校驗(yàn)的方法實(shí)例詳解

    Spring boot進(jìn)行參數(shù)校驗(yàn)的方法實(shí)例詳解

    這篇文章主要介紹了Spring boot進(jìn)行參數(shù)校驗(yàn)的方法實(shí)例詳解,非 常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧
    2018-05-05
  • springboot項(xiàng)目打包并部署到Tomcat上及報(bào)錯(cuò)處理方案

    springboot項(xiàng)目打包并部署到Tomcat上及報(bào)錯(cuò)處理方案

    這篇文章主要介紹了springboot項(xiàng)目打包并部署到Tomcat上及報(bào)錯(cuò)處理方案,本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2024-08-08
  • java利用pdfbox+poi往pdf插入數(shù)據(jù)

    java利用pdfbox+poi往pdf插入數(shù)據(jù)

    這篇文章主要給大家介紹了關(guān)于java利用pdfbox+poi如何往pdf插入數(shù)據(jù)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2022-02-02
  • Java使用pdfbox實(shí)現(xiàn)給pdf文件加圖片水印

    Java使用pdfbox實(shí)現(xiàn)給pdf文件加圖片水印

    有時(shí)候需要給pdf加水印,市面上工具都是收費(fèi)的要會(huì)員,還是自食其力吧;嘗試過(guò) spire.pdf.free 那個(gè)超過(guò)10頁(yè)就不行了!所以本文還是使用了pdfbox,感興趣的可以了解一下
    2022-11-11

最新評(píng)論