Mybatis如何使用ognl表达式实现动态sql

作者:张财华 时间:2021-06-22 03:34:49 

本文讲述在mybatis中如何使用ognl表达式实现动态组装sql语句

新建Users实体类:


public class Users {
   private Integer uid;
   private String userName;
   private String tel;
   //添加上面私有字段的get、set方法
}

新建一个Dao接口类,mybatis配置文件在配置namespace属性时需要加入这个类的完整类名,找到这个类里的方法执行:


public interface UserDao {
   /**
    * 依据userName查询用户信息
    * @param user
    * @return
    */
   List<Users> listUser(Users user);
   /**
    * 动态选择条件
    * @param user
    * @return
    */
   List<Users> listUser2(Users user);
   /**
    * 动态范围查询
    * @param uids
    * @return
    */
   List<Users> listUser3(Integer[] uids);
   /**
    * 动态更新用户信息
    * @param user
    */
   void updateUser(Users user);
   /**
    * 批量添加
    * @param list
    */
   void addBatch(List<Users> list);
   /**
    * 批量删除
    * @param ids
    */
   void deleteBatch(int[] ids);
   /**
    * 批量更新
    * @param list
    */
   void updateBatch1(List<Users> list);
   /**
    * 批量更新
    * @param list
    */
   void updateBatch2(List<Users> list);
}

新建mybatis的配置文件(下面代码可以作为mybatis配置文件的模板,这里的namespace属性可以设置为上面的dao类的完整类名):


<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
       "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- mapper根节点的namespace指定Dao接口的完整类名,
因为mybatis在解析的时候要根据这个类名找到相应dao方法执行 --><mapper namespace="">   </mapper>

在mybatis配置文件中写一条条件查询sql语句:


<!-- 动态查询(使用if拼接条件,适用于多选多的形式) -->
<select id="listUser" parameterType="users" resultMap="userMap">
   select u_id, u_name, u_tel from user_info4 user_info
   <where>
       <if test="userName != null and userName !=''">
           u_name = #{userName}
       </if>
       <if test="tel != null and tel !=''">
           and u_tel = #{tel}
       </if>
   </where>
</select>

这里使用了where和if标签,<where></where>意思就是sql语句中的where,当然,where直接在sql中写出来也可以,<if></if>标签就是条件判断,test属性中写条件语句,如果test中的条件语句为true,那么标签中的sql语句就会拼接到上面的sql语句中,所以这条sql语句的意思就是如果传过来的Users对象中,userName字段不为null,或字段值不为空,那么就添加一个对userName的查询,tel也是如此。

注意:在对tel字段的判断时,标签中的sql语句前加了一个and,如果前一条判断为false,那么mybatis会自动将and关键字删除。


<!-- 动态查询(使用choose选择条件,适用于多选一) -->
<select id="listUser2" parameterType="users" resultMap="userMap">
   select u_id, u_name, u_tel from user_info4
   <choose>
       <when test="userName != null and userName != ''">
           where u_name = #{userName}
       </when>
       <when test="tel != null and tel != ''">
           where u_tel = #{tel}
       </when>
       <otherwise>
           order by u_id desc
       </otherwise>
   </choose>
</select>

这里使用的是choose-when-otherwise标签,有点类似于java中的switch-case-default选择条件语句,相比于if标签,这里只能选择一个,即多选一。

在这条sql语句中,会按顺序判断when子句,如果所有的when子句都为false,则会执行otherwise子句中的sql语句。


<!-- 动态查询(使用foreach范围查询,适用于in和or语句) -->
<!-- int[] ids = new int[]{1,2,3,4}; -->
<select id="listUser3" parameterType="collection" resultMap="userMap">
   select u_id, u_name, u_tel from user_info4
   <where>
       u_id in
       <if test="array != null">
           <foreach collection="array" item="id" open="(" separator="," close=")">
               #{id}
           </foreach>
       </if>
   </where>
</select>

foreach标签适用于范围查询(in和or语句),如遍历一个id集合,查询出集合中所有id对应的用户。在foreach标签中,collection属性指定需要遍历的集合的名称,这里只有一个参数,所以可以随意取;item指定遍历的每一项的别名,open指定在遍历前需要加上的内容,separator指定每遍历一个后加上的内容,close指定遍历完后需要加上的内容,如遍历上面的ids集合,那么最终得到的内容就是 (1,2,3,4) 。


<!-- 动态更新(使用set动态更新字段) -->
<update id="updateUser" parameterType="users" >
   update user_info4
   <trim prefix="set" suffixOverrides=",">
       <if test="userName != null and userName != ''">
           u_name = #{userName},
       </if>
       <if test="tel != null and tel != ''">
           u_tel = #{tel}
       </if>
   </trim>
   <!-- where u_id = #{uid} -->
   <where>
       u_id = #{uid}
   </where>
</update>

trim标签用于动态设值,例如在更新数据时,可以动态将改变的字段设置。在trim标签中,prefix属性表示在更新字段之前添加set关键字,suffixOverrides表示将最后一个更新字段的逗号替换成suffix指定的空格符,如果不指定suffix默认就是空格。


<!-- 批量添加,利用sql的特性 -->
<insert id="addBatch" parameterType="list">
   insert into user_info4(u_id, u_name, u_tel) values
   <foreach collection="list" item="user" separator=",">
       (#{user.uid}, #{user.userName}, #{user.tel})
   </foreach>
</insert>

foreach标签不仅可以用于范围查询,还可以遍历集合用于批量添加。

因为可以利用sql的特性,例如:insert into user_info4(u_name, u_tel) values('', ''), ('', ''), ('', '');这样执行这条sql语句就可以实现批量添加。


<!-- 批量更新1,这一种方式兼容性较好,当数据量大时 -->
<update id="updateBatch1" parameterType="list">
   <foreach collection="list" item="user" separator=";">
       update user_info4
       <set>
           u_name = #{user.userName}
       </set>
       where u_id = #{user.uid}
   </foreach>
</update>

foreach还可以用于遍历出多条sql语句,使得一次可以执行多条sql,当然,如果需要MySql执行多条批量操作时,需要开启批量查询功能,即在MySql的url中加入 allowMultiQueries=true 。


<!-- 批量更新2,使用MySQL的case when语句 -->
   <update id="updateBatch2" parameterType="list">
       update user_info4 set u_name = case u_id
       <foreach collection="list" item="user" separator=" ">
           when #{user.uid} then #{user.userName}
       </foreach>
       end
       where u_id in
       <foreach collection="list" item="user" open="(" close=")" separator=",">
           #{user.uid}
       </foreach>
   </update>

这里使用的是MySql中的case when语句来更新的,基本语法:


update user_info4 set u_name = case u_id
when 3 then '游客1'
when 4 then '游客2'
end
where u_id in(3,4);

Mybatis中的ognl使用总结

经常在写mapper中用到一些OGNL,但是没怎么总结,使用方法一直模模糊糊的。抽点时间,查了别人的blog,做个简单的总结

1.概念

OGNL,Object Graph Navigation Language,是一种强大的表达式语言,网上搜索这个概念,多是和structs有关的。但是在mybatis中OGNL应用很广的;

2.基本参数

Mybatis中常用的OGNL表达式有以下:

e1 or e2

e1 and e2

e1 == e2,e1 eq e2

e1 != e2,e1 neq e2

e1 lt e2:小于

e1 lte e2:小于等于,其他gt(大于),gte(大于等于)

e1 in e2

e1 not in e2

e1 + e2,e1 * e2,e1/e2,e1 - e2,e1%e2

!e,not e:非,求反

e.method(args)调用对象方法

e.property对象属性值

e1[ e2 ]按索引取值,List,数组和Map

@class@method(args)调用类的静态方法

@class@field调用类的静态字段值

更加详细的介绍可以参考官网的介绍:https://commons.apache.org/proper/commons-ognl/language-guide.html

在一定意义上说,mybatis中的动态sql也是基于OGNL表达式的。其中常用的元素有如下几种:


   if
   choose(when,otherwise)
   trim
   where
   set
   foreach

3.应用;

OGNL在mybatis中的应用,主要有两种;

1)动态SQL表达式;

举个栗子:


<code class="language-xml hljs  has-numbering"><span class="hljs-tag"><<span class="hljs-title">select</span> <span class="hljs-attribute">id</span>=<span class="hljs-value">"demo1"</span> <span class="hljs-attribute">...</span>></span>
</code><pre name="code" class="prettyprint"><code class="language-xml hljs  has-numbering">    select id, name from users
   <span class="hljs-tag"><<span class="hljs-title">bind</span> <span class="hljs-attribute">name</span>=<span class="hljs-value">"nameLike"</span> <span class="hljs-attribute">value</span>=<span class="hljs-value">"'%' + name + '%'"</span>/></span>
   <span class="hljs-tag"><<span class="hljs-title">where</span>></span>
       <span class="hljs-tag"><<span class="hljs-title">if</span> <span class="hljs-attribute">test</span>=<span class="hljs-value">"name != null and name != ''"</span>></span>
           name like '${nameLike}'
       <span class="hljs-tag"></<span class="hljs-title">if</span>></span>
   <span class="hljs-tag"></<span class="hljs-title">where</span>></span>
<span class="hljs-tag"></<span class="hljs-title">select</span>></span></code>

其中的bind中的value值会使用OGNL计算,ps,其中bind的参数调用只能用$获取;

2)${param}参数中


<code class="language-xml hljs  has-numbering"><span class="hljs-tag"><<span class="hljs-title">select</span> <span class="hljs-attribute">id</span>=<span class="hljs-value">"demo2"</span> <span class="hljs-attribute">...</span>></span>
   select id,name from users
   <span class="hljs-tag"><<span class="hljs-title">where</span>></span>
       <span class="hljs-tag"><<span class="hljs-title">if</span> <span class="hljs-attribute">test</span>=<span class="hljs-value">"name != null and name != ''"</span>></span>
           name like '${'%' + name + '%'}'
       <span class="hljs-tag"></<span class="hljs-title">if</span>></span>
   <span class="hljs-tag"></<span class="hljs-title">where</span>></span>
<span class="hljs-tag"></<span class="hljs-title">select</span>></span></code>

此处写的是 ${'%' + name + '%'},而不是 %${name}%,这两种方式的结果一样,但是处理过程不一样。

ps,说明一下#和$的区别:${} 为原样输出,你传什么,sql里就填入什么,比如有引号它也会原样填到sql里。#{} 会使用 PreparedStatement,变量处用 ? 代替。

在能使用 #{} 尽量使用它吧,可以防止sql注入。

以下是一个OGNL的调用静态方法的示例:


   <select id="getRecentQuestionTitle" parameterType="java.lang.String" resultType="java.lang.String">  
           select title from song_question where questionState = #{value}  
           <if test="@Ognl@isSolve(value[0],0)">  
           order by questionTime desc  
           </if>  
           <if test="@Ognl@isSolve(value[0],1)">  
           order by answerTime desc  
           </if>                
           limit 0,1  
     </select>

静态方法如下:


public static boolean isSolve(Object o,String soleState){  
       if(o == null)  
           return false;  
       String str = null;  
       if(o instanceof String[]){  
           String[]objects = (String[])o;  
           str = objects[0];  
       }else if(o instanceof Character){  
           Character c = (Character) o;  
           str =  Character.toString(c);  
       }  
           if(StringUtils.equals(str, soleState))  
               return true;  
       return false;  

}

如果值为0,则order by questionTime desc 根据字段questionTime排序。

如果值为1,则order by answerTime desc根据字段answerTime排序。

来源:https://www.cnblogs.com/zhangcaihua/p/12899061.html

标签:Mybatis,ognl,动态sql
0
投稿

猜你喜欢

  • SpringBoot自定义maven-plugin插件整合asm代码插桩

    2021-05-24 02:15:49
  • Android RxJava创建操作符Interval

    2023-08-14 01:26:24
  • Java程序员必须知道的5个JVM命令行标志

    2023-11-11 15:30:36
  • C#适配器模式的使用

    2022-02-05 15:47:21
  • 新浪微博第三方登录界面上下拉伸图片之第三方开源PullToZoomListViewEx(二)

    2021-07-25 04:52:35
  • Spring Cloud Alibaba Nacos 入门详解

    2022-02-23 12:29:09
  • 深入分析c# 继承

    2023-02-18 21:36:31
  • Android操作SQLite基本用法

    2022-04-28 20:42:02
  • Springboot整合Netty实现RPC服务器的示例代码

    2023-07-14 11:35:35
  • android UI绘制加减号按钮

    2023-09-11 02:55:28
  • Android实现文件下载进度显示功能

    2023-12-26 00:42:28
  • idea导入工程时不能导入maven项目不能加入tomcatServer的原因

    2023-06-13 05:35:11
  • Android studio保存logcat日志到本地的操作

    2022-04-03 23:31:03
  • java连接zookeeper实现zookeeper教程

    2022-09-19 03:04:35
  • Spring Cloud Zuul添加过滤器过程解析

    2023-02-01 06:37:25
  • Netty分布式ByteBuf使用命中缓存的分配解析

    2023-08-31 11:33:49
  • Android基于API的Tabs3实现仿优酷tabhost效果实例

    2021-07-31 18:51:02
  • c#读写App.config,ConfigurationManager.AppSettings 不生效的解决方法

    2021-10-07 22:34:42
  • spring boot配置多个请求服务代理的完整步骤

    2023-03-30 10:30:49
  • java实现数字转大写的方法

    2021-11-04 21:39:45
  • asp之家 软件编程 m.aspxhome.com