最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Mybatis #foreach中相同的變量名導(dǎo)致值覆蓋的問題解決

 更新時(shí)間:2021年07月30日 11:01:43   作者:liupjie  
本文主要介紹了Mybatis #foreach中相同的變量名導(dǎo)致值覆蓋的問題解決,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

背景

使用Mybatis中執(zhí)行如下查詢:

單元測試

@Test
public void test1() {
    String resource = "mybatis-config.xml";
    InputStream inputStream = null;
    try {
        inputStream = Resources.getResourceAsStream(resource);
    } catch (IOException e) {
        e.printStackTrace();
    }
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
        CommonMapper mapper = sqlSession.getMapper(CommonMapper.class);
        QueryCondition queryCondition = new QueryCondition();
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        queryCondition.setWidthList(list);
        System.out.println(mapper.findByCondition(queryCondition));
    }
}

XML

<select id="findByCondition" parameterType="cn.liupjie.pojo.QueryCondition" resultType="cn.liupjie.pojo.Test">
    select * from test
    <where>
        <if test="id != null">
            and id = #{id,jdbcType=INTEGER}
        </if>
        <if test="widthList != null and widthList.size > 0">
            <foreach collection="widthList" open="and width in (" close=")" item="width" separator=",">
                #{width,jdbcType=INTEGER}
            </foreach>
        </if>
        <if test="width != null">
            and width = #{width,jdbcType=INTEGER}
        </if>
    </where>
</select>

打印的SQL:
DEBUG [main] - ==>  Preparing: select * from test WHERE width in ( ? , ? , ? ) and width = ?
DEBUG [main] - ==> Parameters: 1(Integer), 2(Integer), 3(Integer), 3(Integer)

Mybatis版本

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.1</version>
</dependency>

這是公司的老項(xiàng)目,在迭代的過程中遇到了此問題,以此記錄!
PS: 此bug在mybatis-3.4.5版本中已經(jīng)解決。并且Mybatis維護(hù)者也建議不要在item/index中使用重復(fù)的變量名。

問題原因(簡略版)

  • 在獲取到DefaultSqlSession之后,會獲取到Mapper接口的代理類,通過調(diào)用代理類的方法來執(zhí)行查詢
  • 真正執(zhí)行數(shù)據(jù)庫查詢之前,需要將可執(zhí)行的SQL拼接好,此操作在DynamicSqlSource#getBoundSql方法中執(zhí)行
  • 當(dāng)解析到foreach標(biāo)簽時(shí),每次循環(huán)都會緩存一個(gè)item屬性值與變量值之間的映射(如:width:1),當(dāng)foreach標(biāo)簽解析完成后,緩存的參數(shù)映射關(guān)系中就保留了一個(gè)(width:3)
  • 當(dāng)解析到最后一個(gè)if標(biāo)簽時(shí),由于width變量有值,因此if判斷為true,正常執(zhí)行拼接,導(dǎo)致出錯(cuò)
  • 3.4.5版本中,在foreach標(biāo)簽解析完成后,增加了兩行代碼來解決這個(gè)問題。
 //foreach標(biāo)簽解析完成后,從bindings中移除item
  context.getBindings().remove(item);
  context.getBindings().remove(index);

Mybatis流程源碼解析(長文警告,按需自取)

一、獲取SqlSessionFactory

入口,跟著build方法走

//獲取SqlSessionFactory, 解析完成后,將XML中的內(nèi)容封裝到一個(gè)Configuration對象中,
//使用此對象構(gòu)造一個(gè)DefaultSqlSessionFactory對象,并返回
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

來到SqlSessionFactoryBuilder#build方法

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
  try {
    //獲取XMLConfigBuilder,在XMLConfigBuilder的構(gòu)造方法中,會創(chuàng)建XPathParser對象
    //在創(chuàng)建XPathParser對象時(shí),會將mybatis-config.xml文件轉(zhuǎn)換成Document對象
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
    //調(diào)用XMLConfigBuilder#parse方法開始解析Mybatis的配置文件
    return build(parser.parse());
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error building SqlSession.", e);
  } finally {
    ErrorContext.instance().reset();
    try {
      inputStream.close();
    } catch (IOException e) {
      // Intentionally ignore. Prefer previous error.
    }
  }
}

跟著parse方法走,來到XMLConfigBuilder#parseConfiguration方法

private void parseConfiguration(XNode root) {
  try {
    Properties settings = settingsAsPropertiess(root.evalNode("settings"));
    //issue #117 read properties first
    propertiesElement(root.evalNode("properties"));
    loadCustomVfs(settings);
    typeAliasesElement(root.evalNode("typeAliases"));
    pluginElement(root.evalNode("plugins"));
    objectFactoryElement(root.evalNode("objectFactory"));
    objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
    reflectorFactoryElement(root.evalNode("reflectorFactory"));
    settingsElement(settings);
    // read it after objectFactory and objectWrapperFactory issue #631
    environmentsElement(root.evalNode("environments"));
    databaseIdProviderElement(root.evalNode("databaseIdProvider"));
    typeHandlerElement(root.evalNode("typeHandlers"));
    //這里解析mapper
    mapperElement(root.evalNode("mappers"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  }
}

來到mapperElement方法

//本次mappers配置:<mapper resource="xml/CommomMapper.xml"/>
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) {
          //因此走這里,讀取xml文件,并開始解析
          ErrorContext.instance().resource(resource);
          InputStream inputStream = Resources.getResourceAsStream(resource);
          //這里同上文創(chuàng)建XMLConfigBuilder對象一樣,在內(nèi)部構(gòu)造時(shí),也將xml文件轉(zhuǎn)換為了一個(gè)Document對象
          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.");
        }
      }
    }
  }
}

XMLMapperBuilder類,負(fù)責(zé)解析SQL語句所在XML中的內(nèi)容

//parse方法
public void parse() {
  if (!configuration.isResourceLoaded(resource)) {
    //解析mapper標(biāo)簽
    configurationElement(parser.evalNode("/mapper"));
    configuration.addLoadedResource(resource);
    bindMapperForNamespace();
  }

  parsePendingResultMaps();
  parsePendingChacheRefs();
  parsePendingStatements();
}

//configurationElement方法
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");
    }
    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"));
    //解析各種類型的SQL語句:select|insert|update|delete
    buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
  }
}

private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
  for (XNode context : list) {
    //創(chuàng)建XMLStatementBuilder對象
    final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
    try {
      //解析
      statementParser.parseStatementNode();
    } catch (IncompleteElementException e) {
      configuration.addIncompleteStatement(statementParser);
    }
  }
}

XMLStatementBuilder負(fù)責(zé)解析單個(gè)select|insert|update|delete節(jié)點(diǎn)

public void parseStatementNode() {
  String id = context.getStringAttribute("id");
  String databaseId = context.getStringAttribute("databaseId");
  //判斷databaseId是否匹配,將namespace+'.'+id拼接,判斷是否已經(jīng)存在此id
  if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
    return;
  }

  Integer fetchSize = context.getIntAttribute("fetchSize");
  Integer timeout = context.getIntAttribute("timeout");
  String parameterMap = context.getStringAttribute("parameterMap");
  //獲取參數(shù)類型
  String parameterType = context.getStringAttribute("parameterType");
  //獲取參數(shù)類型的class對象
  Class<?> parameterTypeClass = resolveClass(parameterType);
  String resultMap = context.getStringAttribute("resultMap");
  String resultType = context.getStringAttribute("resultType");
  String lang = context.getStringAttribute("lang");
  LanguageDriver langDriver = getLanguageDriver(lang);
  //獲取resultType的class對象
  Class<?> resultTypeClass = resolveClass(resultType);
  String resultSetType = context.getStringAttribute("resultSetType");
  StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
  ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
  //獲取select|insert|update|delete類型
  String nodeName = context.getNode().getNodeName();
  SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
  boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
  boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
  boolean useCache = context.getBooleanAttribute("useCache", isSelect);
  boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

  // Include Fragments before parsing
  XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
  includeParser.applyIncludes(context.getNode());

  // Parse selectKey after includes and remove them.
  processSelectKeyNodes(id, parameterTypeClass, langDriver);

  // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
  //獲取SqlSource對象,langDriver為默認(rèn)的XMLLanguageDriver,在new Configuration時(shí)設(shè)置
  //若sql中包含元素節(jié)點(diǎn)或$,則返回DynamicSqlSource,否則返回RawSqlSource
  SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
  String resultSets = context.getStringAttribute("resultSets");
  String keyProperty = context.getStringAttribute("keyProperty");
  String keyColumn = context.getStringAttribute("keyColumn");
  KeyGenerator keyGenerator;
  String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
  keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
  if (configuration.hasKeyGenerator(keyStatementId)) {
    keyGenerator = configuration.getKeyGenerator(keyStatementId);
  } else {
    keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
        configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
        ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
  }

  builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
      fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
      resultSetTypeEnum, flushCache, useCache, resultOrdered,
      keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}

二、獲取SqlSession

由上文可知,此處的SqlSessionFactory使用的是DefaultSqlSessionFactory

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
  Transaction tx = null;
  try {
    final Environment environment = configuration.getEnvironment();
    final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
    tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
    //創(chuàng)建執(zhí)行器,默認(rèn)是SimpleExecutor
    //如果在配置文件中開啟了緩存(默認(rèn)開啟),則是CachingExecutor
    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();
  }
}

這里獲取到了一個(gè)DefaultSqlSession對象

三、執(zhí)行SQL

獲取CommonMapper的對象,這里CommonMapper是一個(gè)接口,因此是一個(gè)代理對象,代理類是MapperProxy

org.apache.ibatis.binding.MapperProxy@72cde7cc

執(zhí)行Query方法,來到MapperProxy的invoke方法

@Override
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);
  //執(zhí)行操作:select|insert|update|delete
  return mapperMethod.execute(sqlSession, args);
}

執(zhí)行操作時(shí),根據(jù)SELECT操作,以及返回值類型(反射方法獲?。┐_定executeForMany方法

caseSELECT:
  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);
  }
  break;

來到executeForMany方法中,就可以看到執(zhí)行查詢的操作,由于這里沒有進(jìn)行分頁查詢,因此走else

if (method.hasRowBounds()) {
  RowBounds rowBounds = method.extractRowBounds(args);
  result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
} else {
  result = sqlSession.<E>selectList(command.getName(), param);
}

來到DefaultSqlSession#selectList方法中

@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
  try {
    //根據(jù)key(namespace+"."+id)來獲取MappedStatement對象
    //MappedStatement對象中封裝了解析好的SQL信息
    MappedStatement ms = configuration.getMappedStatement(statement);
    //通過CachingExecutor#query執(zhí)行查詢
    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();
  }
}

CachingExecutor#query

@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
 //解析SQL為可執(zhí)行的SQL
 BoundSql boundSql = ms.getBoundSql(parameter);
 //獲取緩存的key
 CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
 //執(zhí)行查詢
 return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}

MappedStatement#getBoundSql

public BoundSql getBoundSql(Object parameterObject) {
 //解析SQL
  BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
  List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
  if (parameterMappings == null || parameterMappings.isEmpty()) {
    boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
  }

  //檢查是否有嵌套的ResultMap
  // 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;
}

由上文,此次語句由于SQL中包含元素節(jié)點(diǎn),因此是DynamicSqlSource。由此來到DynamicSqlSource#getBoundSql。
rootSqlNode.apply(context);這段代碼便是在執(zhí)行SQL解析。

@Override
public BoundSql getBoundSql(Object parameterObject) {
  DynamicContext context = new DynamicContext(configuration, parameterObject);
  //執(zhí)行SQL解析
  rootSqlNode.apply(context);
  SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
  Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
  SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
  BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
  for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
    boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
  }
  return boundSql;
}

打上斷點(diǎn),跟著解析流程,來到解析foreach標(biāo)簽的代碼,F(xiàn)orEachSqlNode#apply

@Override
public boolean apply(DynamicContext context) {
  Map<String, Object> bindings = context.getBindings();
  final Iterable<?> iterable = evaluator.evaluateIterable(collectionExpression, bindings);
  if (!iterable.iterator().hasNext()) {
    return true;
  }
  boolean first = true;
  //解析open屬性
  applyOpen(context);
  int i = 0;
  for (Object o : iterable) {
    DynamicContext oldContext = context;
    if (first) {
      context = new PrefixedContext(context, "");
    } else if (separator != null) {
      context = new PrefixedContext(context, separator);
    } else {
        context = new PrefixedContext(context, "");
    }
    int uniqueNumber = context.getUniqueNumber();
    // Issue #709
    //集合中的元素是Integer,走else
    if (o instanceof Map.Entry) {
      @SuppressWarnings("unchecked")
      Map.Entry<Object, Object> mapEntry = (Map.Entry<Object, Object>) o;
      applyIndex(context, mapEntry.getKey(), uniqueNumber);
      applyItem(context, mapEntry.getValue(), uniqueNumber);
    } else {
      //使用index屬性
      applyIndex(context, i, uniqueNumber);
      //使用item屬性
      applyItem(context, o, uniqueNumber);
    }
    //當(dāng)foreach中使用#號時(shí),會將變量替換為占位符(類似__frch_width_0)(StaticTextSqlNode)
    //當(dāng)使用$符號時(shí),會將值直接拼接到SQL中(TextSqlNode)
    contents.apply(new FilteredDynamicContext(configuration, context, index, item, uniqueNumber));
    if (first) {
      first = !((PrefixedContext) context).isPrefixApplied();
    }
    context = oldContext;
    i++;
  }
  applyClose(context);
  return true;
}

private void applyItem(DynamicContext context, Object o, int i) {
    if (item != null) {
        //在參數(shù)映射中綁定item屬性值與集合值的關(guān)系
        //第一次:(width:1)
        //第二次:(width:2)
        //第三次:(width:3)
        context.bind(item, o);
        //在參數(shù)映射中綁定處理后的item屬性值與集合值的關(guān)系
        //第一次:(__frch_width_0:1)
        //第二次:(__frch_width_1:2)
        //第三次:(__frch_width_2:3)
        context.bind(itemizeItem(item, i), o);
    }
  }

到這里,結(jié)果就清晰了,在解析foreach標(biāo)簽時(shí),每次循環(huán)都會將item屬性值與參數(shù)集合中的值進(jìn)行綁定,到最后就會保留(width:3)的映射關(guān)系,而在解析完foreach標(biāo)簽后,會解析最后一個(gè)if標(biāo)簽,此時(shí)在判斷if標(biāo)簽是否成立時(shí),答案是true,因此最終拼接出來一個(gè)錯(cuò)誤的SQL。

在3.4.5版本中,代碼中增加了context.getBindings().remove(item);在foreach標(biāo)簽解析完成后移除bindings中的參數(shù)映射。以下是源碼:

@Override
public boolean apply(DynamicContext context) {
  Map<String, Object> bindings = context.getBindings();
  final Iterable<?> iterable = evaluator.evaluateIterable(collectionExpression, bindings);
  if (!iterable.iterator().hasNext()) {
    return true;
  }
  boolean first = true;
  applyOpen(context);
  int i = 0;
  for (Object o : iterable) {
    DynamicContext oldContext = context;
    if (first || separator == null) {
      context = new PrefixedContext(context, "");
    } else {
      context = new PrefixedContext(context, separator);
    }
    int uniqueNumber = context.getUniqueNumber();
    // Issue #709
    if (o instanceof Map.Entry) {
      @SuppressWarnings("unchecked")
      Map.Entry<Object, Object> mapEntry = (Map.Entry<Object, Object>) o;
      applyIndex(context, mapEntry.getKey(), uniqueNumber);
      applyItem(context, mapEntry.getValue(), uniqueNumber);
    } else {
      applyIndex(context, i, uniqueNumber);
      applyItem(context, o, uniqueNumber);
    }
    contents.apply(new FilteredDynamicContext(configuration, context, index, item, uniqueNumber));
    if (first) {
      first = !((PrefixedContext) context).isPrefixApplied();
    }
    context = oldContext;
    i++;
  }
  applyClose(context);
  //foreach標(biāo)簽解析完成后,從bindings中移除item
  context.getBindings().remove(item);
  context.getBindings().remove(index);
  return true;
}

到此這篇關(guān)于Mybatis #foreach中相同的變量名導(dǎo)致值覆蓋的問題解決的文章就介紹到這了,更多相關(guān)Mybatis #foreach相同變量名覆蓋內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java實(shí)現(xiàn)收藏功能

    java實(shí)現(xiàn)收藏功能

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)收藏功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • Java中Lambda表達(dá)式的進(jìn)化之路詳解

    Java中Lambda表達(dá)式的進(jìn)化之路詳解

    本文通過示例大家給大家介紹了Java中Lambda表達(dá)式的進(jìn)化之路,感興趣的的朋友一起看看吧,希望能夠給你帶來幫助
    2021-11-11
  • 最全LocalDateTime、LocalDate、Date、String相互轉(zhuǎn)化的方法

    最全LocalDateTime、LocalDate、Date、String相互轉(zhuǎn)化的方法

    大家在開發(fā)過程中必不可少的和日期打交道,對接別的系統(tǒng)時(shí),時(shí)間日期格式不一致,每次都要轉(zhuǎn)化,本文為大家準(zhǔn)備了最全的LocalDateTime、LocalDate、Date、String相互轉(zhuǎn)化方法,需要的可以參考一下
    2023-06-06
  • tio-boot框架整合ehcache實(shí)現(xiàn)過程示例

    tio-boot框架整合ehcache實(shí)現(xiàn)過程示例

    這篇文章主要為大家介紹了tio-boot框架整合ehcache實(shí)現(xiàn)過程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • 利用MyBatis-Plus靈活處理JSON字段的技巧與最佳實(shí)踐

    利用MyBatis-Plus靈活處理JSON字段的技巧與最佳實(shí)踐

    這篇文章主要給大家介紹了關(guān)于利用MyBatis-Plus靈活處理JSON字段的技巧與最佳實(shí)踐,Mybatis-Plus可以很方便地處理JSON字段,在實(shí)體類中可以使用@JSONField注解來標(biāo)記JSON字段,需要的朋友可以參考下
    2024-07-07
  • 如何解決java.lang.NoClassDefFoundError:Could not initialize class java.awt.Color問題

    如何解決java.lang.NoClassDefFoundError:Could not initi

    文章講述了在Java服務(wù)器中處理圖形元素時(shí)遇到的常見問題,即需要運(yùn)行X-server,通過在Tomcat/bin/catalina.sh中增加JAVA_OPTS環(huán)境變量并設(shè)置-Djava.awt.headless=true,可以解決這個(gè)問題,使服務(wù)器能夠在沒有圖形界面的情況下運(yùn)行
    2024-11-11
  • Java計(jì)算幾何圖形面積的實(shí)例代碼

    Java計(jì)算幾何圖形面積的實(shí)例代碼

    這篇文章主要介紹了Java計(jì)算幾何圖形面積的實(shí)例代碼,需要的朋友可以參考下
    2014-03-03
  • 對SpringBoot項(xiàng)目Jar包進(jìn)行加密防止反編譯

    對SpringBoot項(xiàng)目Jar包進(jìn)行加密防止反編譯

    最近項(xiàng)目要求部署到其他公司的服務(wù)器上,但是又不想將源碼泄露出去,要求對正式環(huán)境的啟動包進(jìn)行安全性處理,防止客戶直接通過反編譯工具將代碼反編譯出來,本文介紹了如何對SpringBoot項(xiàng)目Jar包進(jìn)行加密防止反編譯,需要的朋友可以參考下
    2023-10-10
  • Java獲取兩個(gè)集合List的交集、補(bǔ)集、并集(相加)和差集(相減)的幾種方式

    Java獲取兩個(gè)集合List的交集、補(bǔ)集、并集(相加)和差集(相減)的幾種方式

    這篇文章主要介紹了Java獲取兩個(gè)集合List的交集、補(bǔ)集、并集(相加)和差集(相減)的幾種方式,文中通過代碼示例講解的非常詳細(xì),對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2025-04-04
  • 使用Files.walkFileTree遍歷目錄文件

    使用Files.walkFileTree遍歷目錄文件

    這篇文章主要介紹了使用Files.walkFileTree遍歷目錄文件,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-10-10

最新評論

正阳县| 昆山市| 清苑县| 镇安县| 锦州市| 西充县| 内丘县| 云梦县| 肇庆市| 富源县| 贡觉县| 贵阳市| 宝兴县| 慈溪市| 南投县| 宣城市| 中西区| 南木林县| 南木林县| 高碑店市| 黄龙县| 蓝田县| 临夏市| 西乡县| 昭觉县| 炎陵县| 合作市| 北碚区| 中方县| 区。| 盘山县| 屏东县| 景德镇市| 永泰县| 河间市| 沂水县| 乐亭县| 赞皇县| 吴堡县| 抚松县| 营山县|