diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bf418000..74c8165d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ ### Bug修复# * 【core 】 修复原始类型转换时,转换失败没有抛出异常的问题 * 【core 】 修复BeanUtil.mapToBean中bean的class非空构造无法实例化问题 +* 【core 】 修复NamedSql多个连续变量出现替换问题 ------------------------------------------------------------------------------------------------------------- diff --git a/hutool-db/src/main/java/cn/hutool/db/sql/NamedSql.java b/hutool-db/src/main/java/cn/hutool/db/sql/NamedSql.java index 5899c29e5..09480669d 100644 --- a/hutool-db/src/main/java/cn/hutool/db/sql/NamedSql.java +++ b/hutool-db/src/main/java/cn/hutool/db/sql/NamedSql.java @@ -1,6 +1,8 @@ package cn.hutool.db.sql; +import cn.hutool.core.map.MapUtil; import cn.hutool.core.text.StrBuilder; +import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.StrUtil; import java.util.LinkedList; @@ -12,8 +14,8 @@ import java.util.Map; * 支持的占位符格式为: *
  * 1、:name
- * 2、?name
- * 3、@name
+ * 2、@name
+ * 3、?name
  * 
* * @author looly @@ -21,6 +23,8 @@ import java.util.Map; */ public class NamedSql { + private static final char[] NAME_START_CHARS = {':', '@', '?'}; + private String sql; private final List params; @@ -69,6 +73,11 @@ public class NamedSql { * @param paramMap 名和参数的对应Map */ private void parse(String namedSql, Map paramMap) { + if(MapUtil.isEmpty(paramMap)){ + this.sql = namedSql; + return; + } + int len = namedSql.length(); final StrBuilder name = StrUtil.strBuilder(); @@ -77,51 +86,66 @@ public class NamedSql { Character nameStartChar = null; for (int i = 0; i < len; i++) { c = namedSql.charAt(i); - if (c == ':' || c == '@' || c == '?') { + if (ArrayUtil.contains(NAME_START_CHARS, c)) { nameStartChar = c; + + // 新的变量开始符出现,要处理之前的变量 + replaceVar(nameStartChar, name, sqlBuilder, paramMap); } else if (null != nameStartChar) { // 变量状态 if (isGenerateChar(c)) { // 变量名 name.append(c); } else { - // 变量结束 - String nameStr = name.toString(); - if(paramMap.containsKey(nameStr)) { - // 有变量对应值(值可以为null),替换占位符 - final Object paramValue = paramMap.get(nameStr); - sqlBuilder.append('?'); - this.params.add(paramValue); - } else { - // 无变量对应值,原样输出 - sqlBuilder.append(nameStartChar).append(name); - } + // 非标准字符也非变量开始的字符出现表示变量名结束,开始替换 + replaceVar(nameStartChar, name, sqlBuilder, paramMap); nameStartChar = null; - name.clear(); sqlBuilder.append(c); } } else { + // 变量以外的字符原样输出 sqlBuilder.append(c); } } + // 收尾,如果SQL末尾存在变量,处理之 if (false == name.isEmpty()) { - // SQL结束依旧有变量名存在,说明变量位于末尾 - final Object paramValue = paramMap.get(name.toString()); - if (null != paramValue) { - // 有变量对应值,替换占位符 - sqlBuilder.append('?'); - this.params.add(paramValue); - } else { - // 无变量对应值,原样输出 - sqlBuilder.append(nameStartChar).append(name); - } - name.clear(); + replaceVar(nameStartChar, name, sqlBuilder, paramMap); } this.sql = sqlBuilder.toString(); } + /** + * 替换变量,如果无变量,原样输出到SQL中去 + * + * @param nameStartChar 变量开始字符 + * @param name 变量名 + * @param sqlBuilder 结果SQL缓存 + * @param paramMap 变量map(非空) + */ + private void replaceVar(Character nameStartChar, StrBuilder name, StrBuilder sqlBuilder, Map paramMap){ + if(name.isEmpty()){ + // 无变量,按照普通字符处理 + return; + } + + // 变量结束 + final String nameStr = name.toString(); + if(paramMap.containsKey(nameStr)) { + // 有变量对应值(值可以为null),替换占位符为?,变量值放入相应index位置 + final Object paramValue = paramMap.get(nameStr); + sqlBuilder.append('?'); + this.params.add(paramValue); + } else { + // 无变量对应值,原样输出 + sqlBuilder.append(nameStartChar).append(name); + } + + //清空变量,表示此变量处理结束 + name.clear(); + } + /** * 是否为标准的字符,包括大小写字母、下划线和数字 * diff --git a/hutool-db/src/test/java/cn/hutool/db/NamedSqlTest.java b/hutool-db/src/test/java/cn/hutool/db/NamedSqlTest.java index 003902e42..de324333a 100644 --- a/hutool-db/src/test/java/cn/hutool/db/NamedSqlTest.java +++ b/hutool-db/src/test/java/cn/hutool/db/NamedSqlTest.java @@ -15,7 +15,11 @@ public class NamedSqlTest { public void parseTest() { String sql = "select * from table where id=@id and name = @name1 and nickName = :subName"; - Map paramMap = MapUtil.builder("name1", (Object)"张三").put("age", 12).put("subName", "小豆豆").build(); + Map paramMap = MapUtil + .builder("name1", (Object)"张三") + .put("age", 12) + .put("subName", "小豆豆") + .build(); NamedSql namedSql = new NamedSql(sql, paramMap); //未指定参数原样输出 @@ -28,7 +32,12 @@ public class NamedSqlTest { public void parseTest2() { String sql = "select * from table where id=@id and name = @name1 and nickName = :subName"; - Map paramMap = MapUtil.builder("name1", (Object)"张三").put("age", 12).put("subName", "小豆豆").put("id", null).build(); + Map paramMap = MapUtil + .builder("name1", (Object)"张三") + .put("age", 12) + .put("subName", "小豆豆") + .put("id", null) + .build(); NamedSql namedSql = new NamedSql(sql, paramMap); Assert.assertEquals("select * from table where id=? and name = ? and nickName = ?", namedSql.getSql()); @@ -38,6 +47,19 @@ public class NamedSqlTest { Assert.assertEquals("小豆豆", namedSql.getParams()[2]); } + @Test + public void parseTest3() { + // 测试连续变量名出现是否有问题 + String sql = "SELECT to_char(sysdate,'yyyy-mm-dd hh24:mi:ss') as sysdate FROM dual"; + + Map paramMap = MapUtil + .builder("name1", (Object)"张三") + .build(); + + NamedSql namedSql = new NamedSql(sql, paramMap); + Assert.assertEquals(sql, namedSql.getSql()); + } + @Test public void queryTest() throws SQLException { Map paramMap = MapUtil