好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

MyBatis执行动态SQL的方法

大家基本上都知道如何使用 mybatis 执行任意 sql,使用方法很简单,例如在一个 xxmapper.xml 中:

?

1

2

3

<select id= "executesql" resulttype= "map" >

   ${_parameter}

</select>

你可以如下调用:

?

1

sqlsession.selectlist( "executesql" , "select * from sysuser where enabled = 1" );

或者你可以在 xxmapper.java 接口中定义如下方法:

?

1

list<map> executesql(string sql);

然后使用接口调用方法:

?

1

xxmapper.executesql( "select * from sysuser where enabled = 1" );

上面这些内容可能都会,下面在此基础上再复杂一点。

假如像上面sql中的 enabled = 1 我想使用参数方式传值,也就是写成 enabled = #{enabled} ,如果你没有遇到过类似这种需求,可能不明白为什么要这么写,举个例子,要实现一种动态查询,可以在前台通过配置 sql,提供一些查询条件就能实现一个查询的功能(为了安全,这些配置肯定是开发或者实施做的,不可能让用户直接操作数据库)。

针对这个功能,使用 mybatis 实现起来相当容易。配置 sql 肯定要执行,用上面讲的这种方式肯定可以执行 sql,如何提供参数呢?参数就是 enabled = #{enabled} 中的 #{enabled} 部分。如果再多一些条件,一个配置好的 sql 如下:

?

1

2

3

select * from sysuser

where enabled = #{enabled}

and username like concat( '%' ,#{username}, '%' )

这种情况下,该怎么用 mybatis 实现呢?

首先 xml 中修改如下:

?

1

2

3

<select id= "executesql" resulttype= "map" >

   ${sql}

</select>

接口中的方法修改为:

?

1

list<map> executesql(map map);

然后调用方法:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

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 中的代码:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

@override

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 boundsql;

}

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为基础:

?

1

2

3

4

5

6

7

8

select * from sysuser

where 1 = 1

<# if enabled??>

enabled = #{enabled}

</# if >

<# if username?? && username != '' >

and username like concat( '%' ,#{username}, '%' )

</# if >

注意,这里的 <#if> 是freemarker的元素。在不考虑sql注入的情况下,上面的sql还可以写成:

?

1

2

3

4

5

6

7

8

select * from sysuser

where 1 = 1

<# if enabled??>

enabled = #{enabled}

</# if >

<# if username?? && username != '' >

and username like '%${username}%'

</# if >

区别就是 '%${username}%' ,因为 freemarker 也会处理 ${username} ,也会用实际的值来替换这里的参数。

在前面调用的代码中,这里修改如下:

?

1

2

3

4

5

6

7

8

9

10

11

12

//#{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" , sql);

//执行方法

list<map> list = xxmapper.executesql(map);

注: processsqlbyfreemarker 方法就是根据map中的数据来处理sql字符串,实现方式可以自己搜索。

到这里,一个不是很复杂的动态sql功能就实现了。

不知道有没有更贪心的人,你会不会想,上面返回值都是 list<map> 类型,能不能返回一个我指定的实体类呢?

例如在map中:

?

1

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 的情况容易很多。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对的支持。如果你想了解更多相关内容请查看下面相关链接

原文链接:https://blog.csdn.net/isea533/article/details/52368341

查看更多关于MyBatis执行动态SQL的方法的详细内容...

  阅读:15次