MyBatis(四)动态sql

一 OGNL表达式

OGNL,全称为Object-Graph Navigation Language,它是一个功能强大的表达式语言,用来获取设置Java对象的属性,它旨在提供一个更高的更抽象的层次来对Java对象图进行导航。类似于我们的EL,SpEL等

OGNL表达式的基本单位是"导航链",一般导航链由如下几个部分组成:
(1)属性名称(property)
(2)方法调用(method invoke)
(3)数组元素
所有的OGNL表达式都基于当前对象的上下文来完成求值运算,链的前面部分的结果将作为后面求值的上下文。例如:names[0].length()

常规用法

  • 访问对象属性:person.name
  • 调用方法: person.getName()
  • 调用静态属性/方法:@java.lang.Math@PI@java.util.UUID@randomUUID()
  • 调用构造方法:new com.atguigu.bean.Person(‘admin’).name
  • 运算符: +,-*,/,%
    +逻辑运算符: in,not in,>,>=,<,<=,==,!=
    注意:xml中特殊符号如”,>,<等这些都需要使用转义字符

表格用法

类型 伪属性 伪属性对应的 Java 方法
List、 Set、 Map size、 isEmpty List/Set/Map.size(),List/Set/Map.isEmpty()
List、 Set iterator List.iterator()、 Set.iterator()
Map keys、 values Map.keySet()、 Map.values()
Iterator next、 hasNext Iterator.next()、 Iterator.hasNext()

二 if

    <select id="findUserByIf"  resultType="com.zyc.entity.User" parameterType="com.zyc.entity.User">
         select id,username ,DATE_FORMAT(birthday,'%Y-%m-%d')birthday,sex ,address from t_user
         where 1 = 1
        <if test="id != null">
            and id = #{id}
        </if>
        <if test="username != null">
            and username = #{username}
        </if>
    </select>

三 where

主要是用来简化sql语句中where条件判断的,能智能的处理 and or条件
mybatis使用where标签来将所有的查询条件包括在内(会自动加where关键)。mybatis就会将where标签中拼装的sql,多出来的and或者or去掉(where只会去掉第一个多出来的and或者or)
总结 配合if,剩下无用的 1=1

    <select id="findUserByWhere"  resultType="com.zyc.entity.User" parameterType="com.zyc.entity.User">
        select id,username ,DATE_FORMAT(birthday,'%Y-%m-%d')birthday,sex ,address from t_user
        <where>
            <if test="id != null">
                and id = #{id}
            </if>
            <if test="username != null">
                and username = #{username}
            </if>
        </where>
    </select>

四 trim

后面多出的and或者or where标签不能解决

  • prefix="" 前缀:trim标签体中是整个字符串拼串 后的结果。prefix给拼串后的整个字符串加一个前缀
  • prefixOverrides="" 前缀覆盖: 去掉整个字符串前面多余的字符
  • suffix="" 后缀:suffix给拼串后的整个字符串加一个后缀
  • suffixOverrides="" 后缀覆盖:去掉整个字符串后面多余的字符

这些属性里面可以写一些sql语句表达式

  1. prefixoverride="AND |OR"
  2. suffix=" where id = #{id} "
<select id="dynamicTrimTest" parameterType="Blog" resultType="Blog">
    select * from t_blog 
    <trim prefix="where" prefixOverrides="and |or">
        <if test="title != null">
            title = #{title}
        </if>
        <if test="content != null">
            and content = #{content}
        </if>
        <if test="owner != null">
            or owner = #{owner}
        </if>
    </trim>
</select>

五 choose

相当于java 语言中的 switch ,与 jstl 中的choose 很类似

<select id="dynamicChooseTest" parameterType="Blog" resultType="Blog">
    select * from t_blog where 1 = 1 
    <choose>
        <when test="title != null">
            and title = #{title}
        </when>
        <when test="content != null">
            and content = #{content}
        </when>
        <otherwise>
            and owner = "owner1"
        </otherwise>
    </choose>
</select>

当when中有条件满足的时候,就会跳出choose,即所有的when和otherwise条件中,只有一个会输出,当所有的我很条件都不满足的时候就输出otherwise中的内容

六 set (主要用于更新时)

set元素主要是用在更新操作的时候,它的主要功能和where元素其实是差不多的,主要是在包含的语句输出一个set,然后如果包含的语句是以逗号结束的话将会把该逗号忽略(去掉),如果set包含的内容为空的话则会出错。有了set元素我们就可以动态的更新那些修改了的字段。

     <!--public void updateEmp(Employee employee);  -->
     <update id="updateEmp">
        <!-- Set标签的使用 -->
        update tbl_employee 
        <set>
            <if test="lastName!=null">
                last_name=#{lastName},
            </if>
            <if test="email!=null">
                email=#{email},
            </if>
            <if test="gender!=null">
                gender=#{gender}
            </if>
        </set>
        where id=#{id} 
<!--        
        Trim:更新拼串
        update tbl_employee 
        <trim prefix="set" suffixOverrides=",">
            <if test="lastName!=null">
                last_name=#{lastName},
            </if>
            <if test="email!=null">
                email=#{email},
            </if>
            <if test="gender!=null">
                gender=#{gender}
            </if>
        </trim>
        where id=#{id}  -->
     </update>

七 foreach

在实现 mybatis in 语句查询时特别有用
foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合。foreach元素的属性主要有item,index,collection,open,separator,close。
(1)item表示集合中每一个元素进行迭代时的别名
(2)index 索引。遍历list的时候是index就是索引,item就是当前值;遍历map的时候index表示的就是map的key,item就是map的值
(3)open 遍历出所有结果拼接一个开始的字符
(4)separator 表示在每次进行迭代之间以什么符号作为分隔符
(5)close 遍历出所有结果拼接一个结束的字符
(6)collection 指定要遍历的集合:list类型的参数会特殊处理封装在map中,map的key就叫list

在使用foreach的时候·最关键·的也是·最容易出错·的就是·collection属性·,该属性是·必须指定·的,但是在·不同情况下·,该属性的值是不一样的,主要有一下3种情况:
(1)如果传入的是单参数且参数类型是一个List的时候,collection属性值为list。对应上面的特殊处理
(2)如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array 也是mybatis处理的
(3)如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map,实际上如果你在传入参数的时候,在MyBatis里面也是会把它封装成一个Map的,map的key就是参数名,所以这个时候collection属性值就是传入的List或array对象在自己封装的map里面的key

单参数List的类型

public List<User> dynamicForeachTest(List<Integer> ids)

<select id="dynamicForeachTest" resultType="com.mybatis.entity.User">
    select * from t_user where id in
    <foreach collection="list" index="index" item="item" open="(" separator="," close=")">
        #{item}
    </foreach>
</select>

数组类型的参数

<select id="dynamicForeach2Test" resultType="com.mybatis.entity.User">
    select * from t_user where id in
    <foreach collection="array" index="index" item="item" open="(" separator="," close=")">
        #{item}
    </foreach>
</select>

Map类型的参数

<select id="dynamicForeach3Test" resultType="com.mybatis.entity.User">
    select * from t_user where username like '%${username}%' and id in
    <foreach collection="ids" index="index" item="item" open="(" separator="," close=")">
        #{item}
    </foreach>
</select>

public List<User> dynamicForeach3Test(Map<String, Object> params); 

调用demo

     List<Integer> ids = new ArrayList<Integer>();
    ids.add(1);
    ids.add(2);
    ids.add(6);
    Map map =new HashMap();
    map.put("username", "小");
    map.put("ids", ids);
    List<User> userList = mapper.dynamicForeach3Test(map);
    System.out.println("------------------------");
    for (User user : userList){
        System.out.println(user);
    }

八 bind

bind标签的两个属性都是必选项,name为绑定到上下文的变量名,value为OGNL表达式,创建一个bind标签后,就可以在下面直接使用了。 使用bind拼接字符串不仅可以避免因更换数据库而修改SQL,也能预防SQL注入
<select id="selectSysUserByAdvancedCondition" resultType="com.artisan.mybatis.xml.domain.SysUser">
SELECT
a.id,
a.user_name userName,
a.user_password userPassword,
a.user_email userEmail,
a.user_info userInfo,
a.head_img headImg,
a.create_time createTime
FROM
sys_user a
<where>
<if test="userName != null and userName != '' ">

<bind name="userNameLike" value=" '%' + userName + '%' "/>
and user_name like #{userNameLike}
</if>
<if test="userEmail != null and userEmail != '' ">
and user_email = #{userEmail}
</if>
</where>
</select>

九 sql片段

抽取可重用的sql片段。方便后面引用

  1. ·sql抽取·:·经·常将要查询的列名,或者插入用的·列名·抽取出来方便引用
  2. include引用已经抽取的sql:
  3. include还可以自定义一些property,sql标签内部就能使用自定义的属性
    include-property:取值的正确方式${prop}, #{不能使用这种方式}
      <sql id="insertColumn">
            <if test="_databaseId=='oracle'">
                employee_id,last_name,email
            </if>
            <if test="_databaseId=='mysql'">
                last_name,email,gender,d_id
            </if>
      </sql>

十 批量保存

MySQL

两种方式

     <!--public void addEmps(@Param("emps")List<Employee> emps);  -->
     <!--MySQL下批量保存:可以foreach遍历   mysql支持values(),(),()语法-->
    <insert id="addEmps">
        insert into tbl_employee(
            <include refid="insertColumn"></include>
        ) 
        values
        <foreach collection="emps" item="emp" separator=",">
            (#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id})
        </foreach>
     </insert>

     <!-- 这种方式需要数据库连接属性allowMultiQueries=true;
        这种分号分隔多个sql可以用于其他的批量操作(删除,修改) -->
     <insert id="addEmps">
        <foreach collection="emps" item="emp" separator=";">
            insert into tbl_employee(last_name,email,gender,d_id)
            values(#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id})
        </foreach>
     </insert>

Oracle

    <!-- Oracle数据库批量保存: 
        Oracle不支持values(),(),()
        Oracle支持的批量方式
        1、多个insert放在begin - end里面
            begin
                insert into employees(employee_id,last_name,email) 
                values(employees_seq.nextval,'test_001','test_001@atguigu.com');
                insert into employees(employee_id,last_name,email) 
                values(employees_seq.nextval,'test_002','test_002@atguigu.com');
            end;
        2、利用中间表:
            insert into employees(employee_id,last_name,email)
               select employees_seq.nextval,lastName,email from(
                      select 'test_a_01' lastName,'test_a_e01' email from dual
                      union
                      select 'test_a_02' lastName,'test_a_e02' email from dual
                      union
                      select 'test_a_03' lastName,'test_a_e03' email from dual
               )    
     -->
     <insert id="addEmps" databaseId="oracle">
        <!-- oracle第一种批量方式 -->
        <!-- <foreach collection="emps" item="emp" open="begin" close="end;">
            insert into employees(employee_id,last_name,email) 
                values(employees_seq.nextval,#{emp.lastName},#{emp.email});
        </foreach> -->
        
        <!-- oracle第二种批量方式  -->
        insert into employees(
            <!-- 引用外部定义的sql -->
            <include refid="insertColumn">
                <property name="testColomn" value="abc"/>
            </include>
        )
                <foreach collection="emps" item="emp" separator="union"
                    open="select employees_seq.nextval,lastName,email from("
                    close=")">
                    select #{emp.lastName} lastName,#{emp.email} email from dual
                </foreach>
     </insert>

十一 内置参数

不只是方法传递过来的参数可以被用来判断,取值等操作
mybatis默认还有两个内置参数:

  1. _parameter:代表整个参数
    单个参数:_parameter就是这个参数
    多个参数:参数会被封装为一个map;_parameter就是代表这个map
  2. _databaseId:如果配置了databaseIdProvider标签(主配置文件中)。
    _databaseId就是代表当前数据库的别名oracle
  <select id="getEmpsTestInnerParameter" resultType="com.atguigu.mybatis.bean.Employee">
        <!-- bind:可以将OGNL表达式的值绑定到一个变量中,方便后来引用这个变量的值 -->
        <bind name="_lastName" value="'%'+lastName+'%'"/>
        <if test="_databaseId=='mysql'">
            select * from tbl_employee
            <if test="_parameter!=null">
                where last_name like #{lastName}
            </if>
        </if>
        <if test="_databaseId=='oracle'">
            select * from employees
            <if test="_parameter!=null">
                where last_name like #{_parameter.lastName}
            </if>
        </if>
  </select>

参考

  1. Mybatis学习总结(五)——动态sql
  2. 尚硅谷mybatis教程资料
  3. MyBatis-15MyBatis动态SQL之【bind】
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,692评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,482评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,995评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,223评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,245评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,208评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,091评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,929评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,346评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,570评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,739评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,437评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,037评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,677评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,833评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,760评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,647评论 2 354

推荐阅读更多精彩内容