阅读(1218) (1)

HasorDB 参数与规则

2021-12-28 17:22:17 更新

在 映射文件 和 动态 SQL 两部分内容中,看到了简单的参数传递,例​ #{age}​:

<select id="queryListByAge">
select * from `test_user` where age = #{age}
</select>

参数名是根据 API 的调用时候穿入的,具体可以查阅 Mapper 接口,本节将会介绍 HasorDB 提供的三种不同参数传递方式。

  • #{...}​ 动态参数。
  • ${}​ 字符串替换,使用时要小心 SQL 注入
  • @{} ​规则表达式,灵活使用规则可以减少大量 XML 配置。

动态参数​

在前面例子中 ​#{age}​ 会获取,当前参数对象 的 ​age ​属性,并将它的值传递给 ​PreparedStatement​。 这种传参方式是标准做法,通常我们所指的 SQL 防注入也是通过动态参数来解决。

偶尔我们会希望参数类型以某个特定的 JDBC Type 来传递,那么就可以通过下列方式穿入。

#{property, jdbcType=NUMERIC}

也可以同时指定 ​jdbcType ​和 ​javaType​,设置它们最大的作用是可以帮助我们选择适合的 类型处理器

#{property, javaType=int, jdbcType=NUMERIC}

有些时候需要指定 ​TypeHandler ​比如我们自定义的 ​TypeHandler​,可以选择下面这种方式。

#{property, typeHandler=net.hasor.db.example.mapper.MyTypeHandler}

在执行存储过程通常需要指定输入输出参数,可以通过 ​mode ​属性设置为 out 来确定输出参数,比如:

{call proc_select_user(#{abc, mode=out})}

在调用存储过程时 in 参数 和 out 参数都会通过在调用 API 时的参数来承载。mode 有三个取值 ​in​、​out​、​inout ​其中默认是 ​in

字符串替换​

默认情况下,使用 ​#{}​ 语法将会使用 ​PreparedStatement ​设置动态属性。 虽然这样更安全、更快,而且几乎总是首选,但有时只想直接将未修改的字符串注入 SQL 语句。 例如,对于 ​order by​,你可以这样使用:

order by ${columnName}

规则表达式​

规则表达式的写法为 ​@{<规则名> [, <启用条件OGNL> [, 规则内容 ]]}

一个规则最简单的写法只需要指明规则名即可,例如:

@{ruleName}

一个规则可以满足某个条件才可以被使用,那么可以如下:

@{ruleName, age > 30}

有些规则本身在调用的时候要求穿入一些内容。那么使用的方式如下:

@{ruleName, age > 30, xxxxxxx}

还可以使用下面方法省略条件参数如下:

@{ruleName,, xxxxxxx}

规则函数​

规则 描述
@{include, expr, sqlid} 效果和使用 <include refid="sqlid"/> 标签相同
@{ognl, expr, xxxx} 对 xxxx 进行 OGNL 求值,并把执行结果加入到 SQL 参数中。类型读写请参考 类型映射
@{md5, expr, xxxx} 对 xxxx 进行 OGNL 求值,值结果用 MD5 进行编码然后加入到 SQL 参数中
@{uuid32} 产生一个 32 字符长度的 UUID,并加入到 SQL 参数中
@{uuid36} 产生一个 36 字符长度的 UUID,并加入到 SQL 参数中
@{and, queryExpr} 快速'与'条件规则,详细看下面
@{or, queryExpr} 快速'或'条件规则,详细看下面
@{arg, expr, xxxx} xxxx 的写法与 #{...} 中的内容相同。这是一个内置规则,#{...} 就是它的简化形式
@{text, expr, xxxx} 原样输出 xxxx,这是一个内置规则 HasorDB 内部的一些机制会用到它。

快速条件拼接​

快速条件拼接包含 ​快速'与'条件​ 和​ 快速'或'条件 ​它们是两个规则用于取代简单的 ​if ​标签和简单的 ​foreach ​标签。 如下语句,当参数不为空时候才拼接 sql

<select id="queryUser">
select * from `test_user`
where 1 = 1
<if test="age != null">
and age = #{age}
</if>
</select>

可以简化为快速规则写法,其中​ :age ​为属性名。

<select id="queryUser">
select * from `test_user`
@{and, age = :age}
</select>

例如如下 ​foreach ​操作:

<select id="queryUser">
select * from `test_user`
where
id in <foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>

可以简化为快速规则写法,其中​ :list ​为集合属性名。

<select id="queryUser">
select * from `test_user`
@{and, id in (:list)}
</select>

如果多个简单条件,快速写法将会极大的减少 Mapper 的工作量。

<select id="queryByNameAndAge">
select * from `test_user`
@{and, age = :age}
@{and, name = :name}
@{and, id in (:ids)}
</select>

快速条件拼接(高级玩法)​

快速拼接规则的原理是利用 ​net.hasor.db.jdbc.core.ParsedSql​ 工具类,将规则的条件表达式当作 SQL 片段进行解析。 这部分和 ​JdbcTemplate​ Map 传参是等价的。

当 ​ParsedSql ​的 ​buildValues ​方法返回的参数全部为 ​null ​时快速拼接就会失效,反之则为有效的 SQL 片段。

因此快速拼接规则还可稍微复杂一点

@{and, (age = :age and sex = '1') or (name = :name and id in (:ids)) }

对应的 SQL 为:

(age = ? and sex = '1') or (name = ? and id in (?, ?, ?))

自定义规则函数​

实现一个自定义的规则函数十分简单只需要实现 ​net.hasor.db.dal.dynamic.rule.SqlBuildRule​ 接口并注册到系统中就可以了。

public class MyRule implements SqlBuildRule {
public void executeRule(Map<String, Object> data, DynamicContext context,
SqlBuilder sqlBuilder, String activeExpr,
String ruleValue) {
...
SqlArg arg = new SqlArg(expr, value, sqlMode, jdbcType, javaType, typeHandler);
sqlBuilder.appendSql("?", arg);
}
}

注册规则

RuleRegistry.DEFAULT.register("myrule", new MyRule());

最后就可以愉快的使用它了。

<select id="queryByNameAndAge">
select * from `test_user`
@{myrule, true, xxxx}
</select>