HasorDB 参数与规则
在 映射文件 和 动态 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>