动态SQL指的是在程序运行过程中根据传入的参数或业务条件动态拼接生成的SQL语句,广泛应用于需要灵活适配查询条件的业务场景中,比如多条件筛选的商品查询、可配置的规则引擎等场景。

动态SQL常用执行技巧
1. 参数化查询拼接
参数化查询是动态SQL最推荐的实现方式,通过将SQL模板和参数分离,避免直接拼接字符串带来的风险。以Java中使用JDBC实现动态条件查询为例:
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
public class DynamicSqlDemo {
public List<User> queryUsers(String username, Integer age, Connection conn) throws Exception {
// SQL模板,使用?作为占位符
StringBuilder sql = new StringBuilder("SELECT id, username, age FROM user WHERE 1=1");
List<Object> params = new ArrayList<>();
// 动态拼接条件
if (username != null && !username.isEmpty()) {
sql.append(" AND username = ?");
params.add(username);
}
if (age != null) {
sql.append(" AND age = ?");
params.add(age);
}
PreparedStatement ps = conn.prepareStatement(sql.toString());
// 设置参数
for (int i = 0; i < params.size(); i++) {
ps.setObject(i + 1, params.get(i));
}
ResultSet rs = ps.executeQuery();
List<User> result = new ArrayList<>();
while (rs.next()) {
User user = new User();
user.setId(rs.getInt("id"));
user.setUsername(rs.getString("username"));
user.setAge(rs.getInt("age"));
result.add(user);
}
return result;
}
}
2. 使用框架内置的动态SQL能力
主流的ORM框架都提供了成熟的动态SQL支持,比如MyBatis的<if>、<where>、<foreach>等标签,能够更优雅地实现动态拼接:
<select id="queryUsers" resultType="User">
SELECT id, username, age FROM user
<where>
<if test="username != null and username != ''">
AND username = #{username}
</if>
<if test="age != null">
AND age = #{age}
</if>
<if test="ids != null and ids.size() > 0">
AND id IN
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</if>
<where>
</select>
3. 预编译语句复用
对于重复执行的动态SQL场景,可以提前生成预编译语句模板,后续仅传入不同参数执行,减少SQL解析和编译的开销,提升执行效率。
动态SQL的潜在风险
1. SQL注入风险
如果动态SQL直接拼接用户输入的内容,攻击者可以构造恶意输入改变SQL语义,比如传入用户名' OR '1'='1,拼接后的SQL会变成SELECT * FROM user WHERE username = '' OR '1'='1',从而绕过验证查询到所有数据。以下是危险的拼接示例:
// 危险示例,禁止直接使用字符串拼接用户输入
String username = request.getParameter("username");
String sql = "SELECT * FROM user WHERE username = '" + username + "'";
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
2. 性能损耗风险
如果动态SQL的拼接逻辑过于复杂,或者每次执行都生成全新的SQL语句,会导致数据库无法复用执行计划,频繁进行SQL解析和编译,增加数据库的负担。另外无意义的动态拼接比如固定条件的动态拼接,也会徒增代码复杂度。
3. 逻辑错误风险
动态拼接过程中如果条件判断逻辑不严谨,容易出现SQL语法错误,比如拼接多余的连接符、遗漏必要的空格、条件冲突等,这类问题在测试阶段不容易覆盖,容易引发线上故障。
风险规避方案
- 所有用户输入的参数都通过参数化查询或占位符的方式传入,禁止直接拼接字符串
- 使用成熟的ORM框架动态SQL能力,避免手动拼接SQL
- 对动态SQL的生成逻辑增加单元测试,覆盖各种条件组合场景
- 对用户输入的内容做合法性校验,过滤特殊字符,进一步降低风险
- 定期审计动态SQL相关的代码,排查潜在的注入点
总结
动态SQL能够极大提升业务开发的灵活性,但是使用时需要严格遵循规范,优先选择参数化查询和框架内置的动态SQL能力,避免直接拼接用户输入。同时需要充分认识到动态SQL的潜在风险,做好对应的防护措施,才能在享受动态SQL便利的同时,保障系统的安全性和稳定性。