mybatis框架,java代码中拼接的sql,拼接的 where 条件 不生效。


问题一:代码中拼接的sql,拼接的 where 条件 不生效。

项目中使用了一个框架,可以根据方法中自己拼接的sql,进行查询。

数据库表中有一个字段是 LIST_DATE ,类型是varchar(10),存的是日期,格式: 2025-05-01。

Java 使用springboot、mybatis框架 有一个String类型的变量 sql:

String targetDate = "2025-05-01"; 
"select userId,userName,userDate where userDate > " + targetDate; 

targetDate 是一个String类型的,存的数据是时间 ,如 targetDate = "2025-05-13"。

现在将拼接的 sql,作为条件执行时,

<select id="querySql" resultType = "java.lang.String"> 
${sql} 
</select> 
sql中的targetDate没有生效,怎么解决?
分析

用户直接使用${sql}这种方式,会把整个sql变量的内容直接拼接到SQL语句中,而不会进行预编译或者参数替换。

这种情况下,如果sql变量包含用户输入的内容,可能会有安全风险。

不过这里的问题是targetDate没有生效,可能的原因可能是拼接后的SQL格式有问题,或者日期格式不正确。

​ 比如,假设targetDate是字符串"2025-05-13",那么拼接后的SQL应该是:select userId, userName, userDate where userDate > 2025-05-13。

​ 但是在SQL中,日期应该用单引号括起来,比如userDate > '2025-05-13'。

​ 如果用户没有在拼接的时候加上单引号,那么生成的SQL语句中的日期值会被当作数字或者列名,导致语法错误或者逻辑错误。这时候数据库可能不会报错,但条件不会生效,比如比较的时候可能转换成数字或者其他类型,导致结果不符合预期。

​ 所以,问题可能出在用户拼接targetDate的时候没有添加单引号。例如,正确的SQL应该是userDate > '2025-05-13',而用户现在拼接的是userDate > 2025-05-13,这会导致语法问题或者比较错误。这时候数据库可能不会正确解析这个条件,导致条件失效。

​ 那怎么解决呢?首先,用户需要确保在拼接SQL时,targetDate被单引号包裹。也就是说,sql字符串应该是:

"select userId,userName,userDate where userDate > '" + targetDate + "'"。这样的话,生成的SQL条件部分才会是正确的日期比较。

但是,这样做虽然可能解决问题,但存在SQL注入的风险。因为如果targetDate来自用户输入,恶意用户可能通过构造特定的字符串来注入SQL代码。比如,如果targetDate是"'; DROP TABLE users; --",那么拼接后的SQL就会变成userDate > ''; DROP TABLE users; --',导致执行删除表的操作。

因此,更安全的做法是不要使用字符串拼接的方式来构造SQL语句,而是使用MyBatis的参数替换方式,即使用#{}来代替${}。但在这个用户的场景中,他们可能因为某些原因需要动态构造整个SQL语句,这时候可能需要使用MyBatis的动态SQL功能或者其他方式来处理。

但是用户当前的写法是将整个SQL语句作为变量传入,这确实不太安全,也不推荐。不过如果必须这么做,至少需要确保在拼接时正确地处理字符串和日期格式。

另一个可能性是,用户在使用${sql}的时候,MyBatis在处理时可能没有正确解析变量中的某些字符,或者转义的问题。不过更可能的问题还是出在日期值缺少单引号。

所以,解决方案的第一步应该是检查生成的SQL是否正确。用户可以在执行查询之前,打印出完整的SQL语句,看看是否包含正确的单引号。例如,在Java代码中,将sql变量的值输出到日志中,确认其是否正确。如果发现userDate后面没有单引号,那么就需要在拼接的时候加上。

比如,原来的sql可能是这样的:

String sql = "select userId, userName, userDate where userDate > " + targetDate;

应该改为:

String sql = "select userId, userName, userDate where userDate > '" + targetDate + "'";

这样生成的SQL就会是userDate > '2025-05-13',符合SQL的语法要求。

不过,这样做虽然解决了问题,但仍有SQL注入的风险。更好的做法是避免拼接SQL,而是使用预编译的语句。例如,将条件部分作为参数传递,而不是整个SQL。例如:

在Mapper接口中定义方法:

@Select("select userId, userName, userDate where userDate > #{targetDate}")
List<String> queryByDate(@Param("targetDate") String targetDate);

这样MyBatis会自动处理参数,添加必要的单引号,并且防止SQL注入。

但用户的情况可能是需要动态构造不同的SQL语句,比如条件可能变化,这时候可能需要使用MyBatis的动态SQL,比如标签等,根据不同的条件拼接SQL。

不过用户当前的场景是直接传递完整的SQL语句,这可能是因为需要高度动态的查询,比如用户自定义查询等。这种情况下,虽然不推荐,但必须确保所有变量都正确转义和处理。

综上所述,用户的问题可能出在targetDate没有用单引号包裹,导致生成的SQL语法错误或者比较不正确。解决方法是在拼接的时候添加单引号。但同时需要考虑SQL注入的问题,建议使用参数化查询来替代拼接。

所以,最终的解决方案可能包括两个步骤:

  1. 在拼接SQL时,给targetDate加上单引号,确保生成的SQL语句正确。

  2. 避免使用字符串拼接SQL,改用MyBatis的参数化查询,以提高安全性。

如果用户确实需要动态传递整个SQL语句,那么第一个步骤是必要的。但需要提醒用户注意SQL注入的风险,并尽可能避免这种写法。


解决

为了解决SQL中targetDate未生效的问题,需确保日期值被正确包裹在单引号中,并避免SQL注入风险。

以下是具体步骤:

问题原因

拼接的SQL语句中,日期值targetDate缺少单引号,导致数据库无法正确识别为字符串,进而条件失效。例如生成的SQL可能为:

userDate > 2025-05-13

而正确的形式应为:

userDate > '2025-05-13'

解决方案

方法1:添加单引号(快速修复,但有风险)

在拼接SQL时,为targetDate添加单引号:

String sql = "select userId, userName, userDate where userDate > '" + targetDate + "'";

缺点:存在SQL注入风险,若targetDate来自用户输入,可能被恶意利用。

方法2:使用参数化查询(推荐)

避免拼接SQL,改用MyBatis的参数化传参:

  1. 修改Mapper接口
@Select("SELECT userId, userName, userDate WHERE userDate > #{targetDate}")
List<String> queryByDate(@Param("targetDate") String targetDate);
  1. 调用方法
// 直接传递targetDate参数,无需拼接SQL
List<String> result = mapper.queryByDate(targetDate);

优点:自动处理引号和安全转义,防止SQL注入。

方法3:动态SQL(复杂条件时适用)

若需动态构建查询,使用MyBatis的动态SQL标签:

<select id="queryDynamic" resultType="java.lang.String">
  SELECT userId, userName, userDate
  WHERE
  <if test="targetDate != null">
    userDate > #{targetDate}
  </if>
</select>

总结

  • 紧急修复:按方法1添加单引号,但需评估安全风险。
  • 长期安全:采用方法2或3,利用MyBatis的参数化机制,确保安全且代码清晰。尽量避免直接拼接SQL,以提升应用安全性。
JAVA-技能点
知识点
Mysql