Looly 5 年之前
父节点
当前提交
ed708f730c

+ 1 - 0
CHANGELOG.md

@@ -16,6 +16,7 @@
 ### Bug修复#
 * 【core  】     修复原始类型转换时,转换失败没有抛出异常的问题
 * 【core  】     修复BeanUtil.mapToBean中bean的class非空构造无法实例化问题
+* 【core  】     修复NamedSql多个连续变量出现替换问题
 
 -------------------------------------------------------------------------------------------------------------
 

+ 50 - 26
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;
  * 支持的占位符格式为:
  * <pre>
  * 1、:name
- * 2、?name
- * 3、@name
+ * 2、@name
+ * 3、?name
  * </pre>
  * 
  * @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<Object> params;
 
@@ -69,6 +73,11 @@ public class NamedSql {
 	 * @param paramMap 名和参数的对应Map
 	 */
 	private void parse(String namedSql, Map<String, Object> paramMap) {
+		if(MapUtil.isEmpty(paramMap)){
+			this.sql = namedSql;
+			return;
+		}
+
 		int len = namedSql.length();
 
 		final StrBuilder name = StrUtil.strBuilder();
@@ -77,52 +86,67 @@ 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<String, Object> 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();
+	}
+
+	/**
 	 * 是否为标准的字符,包括大小写字母、下划线和数字
 	 * 
 	 * @param c 字符

+ 24 - 2
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<String, Object> paramMap = MapUtil.builder("name1", (Object)"张三").put("age", 12).put("subName", "小豆豆").build();
+		Map<String, Object> 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<String, Object> paramMap = MapUtil.builder("name1", (Object)"张三").put("age", 12).put("subName", "小豆豆").put("id", null).build();
+		Map<String, Object> 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());
@@ -39,6 +48,19 @@ public class NamedSqlTest {
 	}
 
 	@Test
+	public void parseTest3() {
+		// 测试连续变量名出现是否有问题
+		String sql = "SELECT to_char(sysdate,'yyyy-mm-dd hh24:mi:ss') as sysdate FROM dual";
+
+		Map<String, Object> paramMap = MapUtil
+				.builder("name1", (Object)"张三")
+				.build();
+
+		NamedSql namedSql = new NamedSql(sql, paramMap);
+		Assert.assertEquals(sql, namedSql.getSql());
+	}
+
+	@Test
 	public void queryTest() throws SQLException {
 		Map<String, Object> paramMap = MapUtil
 				.builder("name1", (Object)"王五")