mybatismybatis3 动态sql语句句有什么用

Mybatis根据常量判断去组动态SQL有什么好的方案? - 知乎5被浏览488分享邀请回答0添加评论分享收藏感谢收起MyBatis 执行动态 SQL语句详解
作者:isea533
字体:[ ] 类型:转载 时间:
大家对mybatis执行任意sql语句都了解,那么MyBatis执行动态SQL语句呢?下面脚本之家小编给大家解答下mybatis执行动态sql语句的方法,非常不错,感兴趣的朋友参考下吧
大家基本上都知道如何使用 MyBatis 执行任意 SQL,使用方法很简单,例如在一个 XXMapper.xml 中:
&select id="executeSql" resultType="map"&
${_parameter}
你可以如下调用:
sqlSession.selectList("executeSql", "select * from sysuser where enabled = 1");
或者你可以在 XXMapper.java 接口中定义如下方法:
List&Map& executeSql(String sql);
然后使用接口调用方法:
xxMapper.executeSql("select * from sysuser where enabled = 1");
上面这些内容可能都会,下面在此基础上再复杂一点。
假如像上面SQL中的enabled = 1我想使用参数方式传值,也就是写成 enabled = #{enabled},如果你没有遇到过类似这种需求,可能不明白为什么要这么写,举个例子,要实现一种动态查询,可以在前台通过配置 SQL,提供一些查询条件就能实现一个查询的功能(为了安全,这些配置肯定是开发或者实施做的,不可能让用户直接操作数据库)。
针对这个功能,使用 MyBatis 实现起来相当容易。配置 SQL 肯定要执行,用上面讲的这种方式肯定可以执行 SQL,如何提供参数呢?参数就是enabled = #{enabled}中的#{enabled}部分。如果再多一些条件,一个配置好的 SQL 如下:
select * from sysuser
where enabled = #{enabled}
and userName like concat('%',#{userName},'%')
这种情况下,该怎么用 MyBatis 实现呢?
首先 XML 中修改如下:
&select id="executeSql" resultType="map"&
接口中的方法修改为:
List&Map& executeSql(Map map);
然后调用方法:
Map map = new HashMap();
//这里的 sql 对应 XML 中的 ${sql}
map.put("sql", "select * from sysuser "
+ " where enabled = #{enabled} "
+ " and userName like concat('%',#{userName},'%')");
//#{enabled}
map.put("enabled", 1);
//#{userName}
map.put("userName", "admin");
//接口方式调用
List&Map& list = xxMapper.executeSql(map);
//sqlSession方式调用
sqlSession.selectList("executeSql", map);
有了这个SQL之后,就可以将 enabled 和 userName 作为条件提供给用户。这两个条件显然是必填的。如果是可选的,那该怎么写?
也许有人想到了是不是可以用 MyBatis 中的动态 SQL,使用&if&标签等等?
再回答这个问题前,我们先看处理动态 SQL 的 DynamicSqlSource 中的代码:
public BoundSql getBoundSql(Object parameterObject) {
DynamicContext context = new DynamicContext(configuration, parameterObject);
rootSqlNode.apply(context);
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
Class & ?&parameterType =
parameterObject == null ? Object.class: parameterObject.getClass();
SqlSource sqlSource = sqlSourceParser.parse(context.getSql(),
parameterType, context.getBindings());
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
for (Map.Entry & String, Object & entry: context.getBindings().entrySet()) {
boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
return boundS
MyBatis 处理动态 SQL 时,所有动态 SQL 的标签都会处理为 SqlNode (这里的rootSqlNode)对象,包含${}的也会处理为 TextSqlNode 对象,在上面方法的前两行,就是 MyBatis 处理动态 SQL 的地方。
因此如果我们在${sql} 中的内容包含嵌套的${}和&if&,&where&等标签时,他们在 MyBatis 解析 XML 为 SqlNode 对象时,XML &select& 元素包含的内容只有${sql},只有${sql}会被解析,在运行时这个参数字符串中可能包含的${}和&if&,&where&等标签,但是这都发生在 MyBatis 解析后,因此当这些内容作为字符串中的一部分出现时,他们不会被特殊处理,他们只是SQL中的一部分,只是原样输出(由于数据库不认会报错)无法被处理,因此没法通过 MyBatis 自带的这种方式来写动态 SQL。
在上面的代码中:
sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
这一段代码是处理动态参数(#{})的这个处理在动态 SQL 处理之后, 因此可以在 SQL 中使用这种类型的参数。
既然不能用 MyBatis 动态 SQL 方式,该怎么实现动态 SQL 呢?
这里提供一个简单的思路,在 SQL 中使用模板标记语言来实现动态SQL(例如freemarker),在 SQL 交给 MyBatis 执行之前,使用模板对 SQL 进行处理生成最终执行的 SQL(需要避免处理#{}参数),将这个SQL交给 MyBatis 执行。
举一个Freemarker模板的例子,仍然以上面的SQL为基础:
select * from sysuser
where 1 = 1
&#if enabled??&
enabled = #{enabled}
&#if userName?? && userName != ''&
and userName like concat('%',#{userName},'%')
注意,这里的&#if&是Freemarker的元素。在不考虑SQL注入的情况下,上面的SQL还可以写成:
select * from sysuser
where 1 = 1
&#if enabled??&
enabled = #{enabled}
&#if userName?? && userName != ''&
and userName like '%${userName}%'
区别就是'%${userName}%',因为 Freemarker 也会处理 ${userName},也会用实际的值来替换这里的参数。
在前面调用的代码中,这里修改如下:
//#{enabled}
map.put("enabled", 1);
//#{userName}
map.put("userName", "admin");
//这里的 sql 对应 XML 中的 ${sql}
String sql = "上面两个复杂SQL中的一个";
//使用Freemarker处理sql
sql = processSqlByFreemarker(sql, map);
//将处理后的sql放到map中
map.put("sql", "select * from sysuser "
+ " where enabled = #{enabled} "
+ " and userName like concat('%',#{userName},'%')");
//执行方法
List&Map& list = xxMapper.executeSql(map);
注:processSqlByFreemarker方法就是根据map中的数据来处理sql字符串,实现方式可以自己搜索。
到这里,一个不是很复杂的动态SQL功能就实现了。
不知道有没有更贪心的人,你会不会想,上面返回值都是List&Map&类型,能不能返回一个我指定的实体类呢?
例如在map中:
map.put("class", "tk.mybatis.model.SysUser");
能不能通过这种方式让返回值变成SysUser类型呢?由于这篇文章耗时已经太长,这里就提供一个方案,不深入。
你可以使用拦截器实现,获取 MappedStatement 后,复制一份,然后修改 resultMaps中resultMap的type属性为你指定的class类型就能实现,说起来容易,实际操作起来能有 PageHelper 分页插件 1/10 左右的工作量。
因为这篇是应媳妇要求所写,所以假如媳妇有最后的这个需求,我就协助媳妇实现这个插件,然后再共享出来。
注:如果是动态的update,insert,delete 语句,可以将上面的&select&改为update(不需要使用&delete&和&insert&),返回值用int,比 select 的情况容易很多。
以上所述是小编给大家介绍的MyBatis 执行动态 SQL语句详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具2012年3月 Java大版内专家分月排行榜第三
本帖子已过去太久远了,不再提供回复功能。java web后端(29)
& & &&mybatis 中使用 Mapper.xml里面的配置进行 sql 查询,经常需要动态传递参数,例如我们需要根据用户的姓名来筛选用户时,sql 如下:
上述 sql 中,我们希望 name 后的参数 &Jack& 是动态可变的,即不同的时刻根据不同的姓名来查询用户。在&Mapper.xml文件中使用如下的
sql 可以实现动态传递参数 name:
二、$ 与 #
动态 SQL 是 mybatis 的强大特性之一,也是它优于其他 ORM 框架的一个重要原因。mybatis 在对 sql 语句进行预编译之前,会对 sql 进行动态解析,解析为一个 BoundSql 对象,也是在此处对动态 SQL 进行处理的。在动态 SQL 解析阶段, #{ } 和 ${ } 会有不同的表现。
#{ }:解析为一个 JDBC 预编译语句(prepared statement)的参数标记符。
例如,Mapper.xml中如下的 sql 语句:
动态解析为:
一个 #{ } 被解析为一个参数占位符 ? 。
而${ } 仅仅为一个纯碎的 string 替换,在动态 SQL 解析阶段将会进行变量替换。
例如,Mapper.xml中如下的 sql:
当我们传递的参数为 &Jack& 时,上述 sql 的解析为:
预编译之前的 SQL 语句已经不包含变量了,完全已经是常量数据了。
综上所得, ${ } 变量的替换阶段是在动态 SQL 解析阶段,而 #{ }变量的替换是在 DBMS 中。
1、能使用 #{ } 的地方就用 #{ }
首先这是为了性能考虑的,相同的预编译 sql 可以重复利用。其次,${ } 在预编译之前已经被变量替换了,这会存在 sql 注入问题。例如,如下的 sql:
假如,我们的参数 tableName 为 --,那么 SQL 动态解析阶段之后,预编译之前的 sql 将变为:
-- 之后的语句将作为注释,不起作用,因此本来的一条查询语句偷偷的包含了一个删除表数据的 SQL。
2. 表名作为变量时,必须使用 ${ }
这是因为,表名是字符串,使用 sql 占位符替换字符串时会带上单引号 '',这会导致 sql 语法错误,例如:
预编译之后的sql 变为:
假设我们传入的参数为 tableName = &user& , name = &Jack&,那么在占位符进行变量替换后,sql 语句变为:
上述 sql 语句是存在语法错误的,表名不能加单引号 ''(注意,反引号 ``是可以的)。
四、sql预编译
sql 预编译指的是驱动在发送 sql 语句和参数给 DBMS 之前对 sql 语句进行编译,这样
DBMS 执行 sql 时,就不需要重新编译。
2. 为什么需要预编译
JDBC 中使用对象 PreparedStatement 来抽象预编译语句,使用预编译。预编译阶段可以优化 sql 的执行。预编译之后的 sql 多数情况下可以直接执行,DBMS 不需要再次编译,越复杂的sql,编译的复杂度将越大,预编译阶段可以合并多次操作为一个操作。预编译语句对象可以重复利用。把一个 sql 预编译后产生的 PreparedStatement 对象缓存下来,下次对于同一个sql,可以直接使用这个缓存的 PreparedState 对象。mybatis 默认情况下,将对所有的 sql 进行预编译。
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:8299次
排名:千里之外
原创:25篇
转载:37篇
(1)(1)(3)(2)(37)(11)(6)(1)
(window.slotbydup = window.slotbydup || []).push({
id: '4740887',
container: s,
size: '250,250',
display: 'inlay-fix'

我要回帖

更多关于 mybatis输出sql语句 的文章

 

随机推荐