MyBatisSSM框架开发web项目系列(四) MyBatis之快控制动态SQL

  前言

       
通过前的MyBatis部分学习,已经好用MyBatis独立构建一个数据库程序,基本的增删查改/关联查询等等都得兑现了。简单的单表操作及事关查询在骨子里开的业务流程中必将会起,但是可能不过见面占有一部分,很多工作需要数夹杂着有些亟需我们当后台去判断的参数,举个例,我们着力都达过购物网站,想如果翻开心仪之货品列表,可以由此货品归类筛选,也可以通过商品价位来罗,还足以以依据分类和价格来罗,这里我们好大概的敞亮为通过货品分类及商品价位的淘分别吗select语句中where后面的2单子句,类似category=XXX和price
> xxx and price
<xxx,具体怎么筛选要扣押用户怎么操作。如果按照事先的门径,我们设分别定义三个select方法和sql语句,这个就是干到了一个静态动态的题材,用户的操作、输入等等都是免确定的,即动态的,但是咱事先以sql映射文件中写的语句都是针对性单个操作单个想法去描绘很的,即静态的。这样一来,随着需求以及判的底随地叠加,这个代码量会生吓人。另外一个题材,如果大家有使Java代码拼接过复杂SQL语句经历,应该不见面发格外有利,本人用hibernate的时光吗集成接了HQL,共同点就是是那些分隔符、空格之类的形容起格外烦,也爱出错。MyBatis提供了动态SQL这无异于表征,能而且改善上述两种出状况。

  总览

       
MyBatis提供的动态SQL元素实际就是经过当我们的sql语句被置标签实现动态,具体标签而下图所出示。

       MyBatis 1

       
熟悉jsp、jstl、el表达式那套的,应该针对里面大部分签名都非生疏,也于便于懂,具体用法下分别进行解析。为了做现实的施用状况,将上面元素细分为四组来演示,分别吗【if、where、trim】、【if、set、trim】、【choose、when、otherwise】、【foreach】

  背景

private Integer id;         //主键
private String name;        //姓名
private String gender;      //性别
private Integer age;        //年龄
private String ifIT;        //是否从事IT行业

MyBatis 2

  if、where、trim篇

  1.查询语句子被之if

  以上为我们定义的一个人之属性,数据库中也生一个丁的数据表。现在如需要查询人中之所有男性,同时要输入参数中年年龄不为空,就冲性和年龄查询。在从来不下动态SQL之前,按照我们的纵容有思路,我们用在Mapper接口中定义两个查询方式,同时分别针对许在SQL映射文件中定义两只<select>语句,如下:

<select id="selectPerson1" parameterType="psn" resultMap="personResultMap">
    select * from person where GENDER  = '男'
</select>

<select id="selectPerson2" parameterType="psn" resultMap="personResultMap">
    select * from person where GENDER  = '男' and AGE = #{age}
</select>

  这样一来,随着类似之用越来越多,我们的方式和SQL语句量会加到那个多,并且会发现,其实语句被设有不少重复部分。那么来无产生主意能够而且应针对类似之相干要求,同时削减代码量呢?动态SQL就提供了系的功能实现这些需求,例如上述情景,我们即可只待定义一个术,对应的SQL语句写成如下:

<select id="selectPerson" parameterType="psn" resultMap="personResultMap">
    select * from person where GENDER  = '男'
        <if test="age != null">
            and AGE = #{age}
        </if>
</select>

  以这项<select>我们以确定的(静态的的有些)select * from person where GENDER =
‘男’和后的<if>部分组成起来,通过动态SQL提供的<if>标签为语句预加一重叠判断,test属性值为布尔类型,true或者false,当也true(即确实)时,才见面把<if>标签下之情节增补加至告知句被应也价值,这里的test中不怕判断输入参数中年龄是否为空,不为空则添加【and AGE = #{age}】到【select * from person where GENDER =
‘男’】后面,为空则非加以,这样即便直达了并且满足个别种植要,但仅仅定义了一个道以及一致条SQL。

  2.查询语句被if的where改进

  进一步扩大如果想把where后面的片段都动态化,这里因性为条例,查询时一旦参数中生出无为空的性别值,则基于性查询,反的则查询有,有了前if的学,我们不难写有如下动态SQL:

<select id="selectPerson1" parameterType="psn" resultMap="personResultMap">
    select * from person where
        <if test="gender != null">
            gender = #{gender}
        </if>
</select>

 

  这时候问题来了,当性别不为空时,语句是 select * from person where gender =
#{gender}
,这样还能健康查询有我们纪念使的结果,但是要是性别也空,会发觉告诉句变成了 select * from person where
,这明明是深成一个误的SQL了,为了缓解类似之题材,动态SQL<where>能帮忙我们解决者问题,我们得将上述报告句优化成如下:

<select id="selectPerson1" parameterType="psn" resultMap="personResultMap">
    select * from person
    <where>
        <if test="gender != null">
            gender = #{gender}
        </if>
    </where>
</select>

  这样mybatis在此间见面依据<where>标签中是不是出情来确定要无苟增长where,在这里运用<where>后,如果年龄也空,则前面引发错误的where也非会见现出了。

  3.对where的trim同等转换

  进一步壮大,如果我们询问有多独参数需要看清,根据性与年参数,有了眼前<if>和<where>的询问,我们就算足以描绘起如下SQL:

<select id="selectPerson2" parameterType="psn" resultMap="personResultMap">
    select * from person
    <where>
        <if test="gender != null">
            gender = #{gender}
        </if>
        <if test="age != null">
            and age = #{age}
        </if>
    <where>
</select>

  乍一扣基本没什么毛病了,在此处【性别空、年龄空】、【性别不拖欠、年龄不空】、【性别不拖欠、年龄空】都未曾问题,但是若是【性别空、年龄未呢空】,按理来说语句变成这样 select * from person where and age =
#{age}
,然后如果你动手尝试一下,就见面发觉,并无见面,这为反映了<where>一个强有力的处,它不仅仅会根据元素中内容是否为空决定使无使填补加where,还会见活动过滤掉内容头部的and或者or,另外空格之类的题材吗会见智能处理。

  MyBatis还提供了一如既往种植更活的<trim>标签,在此可以代替<where>,如上面的定义可以改成如下,同样好实现力量:

<select id="selectPerson2" parameterType="psn" resultMap="personResultMap">
    select * from person
    <trim prefix="where" prefixOverrides="and |or " >
        <if test="gender != null">
            and gender = #{gender}
        </if>
        <if test="age != null">
            and age = #{age}
        </if>
    </trim>
</select>

  <trim>标签共有四单特性,分别吗【prefix】、【prefixOverrides】、【suffix】、【suffixOverrides】,prefix代表会面让<trim>标签中情节丰富的前缀,当然前提是内容无为空,prefixOverrides代表前缀过滤,过滤的凡内容之前缀,suffix和suffixOverrides则分级对应后缀。例如地方的语中设性别与年都非也空,<trim>会当补加where前缀的同时,把第一个<if>中的and去丢,这样来贯彻<where>同样的成效。

  if、set、trim篇

  1.翻新报告句被之if

  前面都是查询,我们换个创新摸索,这里更新我们特想翻新部分字段,而且如果依据参数是否也空来确定是不是更新,这里因人名以及性也例。

<update id="updatePerson">
    update person set
    <if test="name != null">
        NAME = #{name},
    </if>
    <if test="gender != null">
        GENDER = #{gender}
    </if>
</update>

  2.创新语句中if的set改进

  这里而【姓别为空】或者【性别和性都也空】,类似事先的where问题一样为来了,所以MyBatis同样也提供了<set>标签来缓解当时无异于问题,所以地方的定义可以优化成如下所示

<update id="updatePerson1">
    update person
    <set>
     <if test="name != null">
            NAME = #{name},
        </if>
        <if test="gender != null">
            GENDER = #{gender},
        </if>
    </set>
</update>

   以此,<set>会冲签中内容的起管来确定要无苟长set,同时会活动恢复内容后缀逗号,但是有好几如果专注,不同为<where>,当<where>中情节也空时,我们可以识破有人数的音讯,但是此地更新报告句被,<set>内容吧空时,语句变成 update person
,还是会出错,同时更新操作为未会见立竿见影了。

  3.针对set的trim同等转换

  之前介绍到之<trim>灵活的处在,在此透过改属性值,也能为此来代替<set>,上面的概念使用<trim>改写如下所示:

<update id="updatePerson2">
    update person
    <trim prefix="set" suffixOverrides=",">
        <if test="name != null">
            NAME = #{name},
        </if>
        <if test="gender != null">
            GENDER = #{gender}
        </if>
    </trim>
</update>

  这里设置前缀为set,后缀过滤为逗号“,”,这样一来,在if判断之后会吸引报错的逗号将不会见设有,同样好兑现相关力量。

  choose、when、otherwise篇

  前面我们了解及之,<select>查询语句中where后面,<if>通过判断各个参数是否也空来确定是不是添加其子词内容到where后面,是一个加上的涉,但是倘若我们的急需,不是增长,而是多选择同,例如姓名(name)、性别(gender)、是否从It行业(ifIT),具体来讲就是,如果先判断姓名是否来价,有的言语就因姓名查,没有底口舌其次再判断性别是否发生价,有的话就根据性别查,也绝非底言语虽因是否从IT行业来查阅。这样一来,按照前面了解及之<if><where>似乎有些头怪,不仅起差不多更判,还提到到一个先级先后问题,一下子犹如特别为难快速想到一个简练方便的路线。MyBatis同样提供了一如既往模拟<choose>、<when>、<otherwise>来支援咱缓解类似上面的题材。

  如果看无绝好记,可以联想Java中标准判断的switch,switch对应这里的<choose>,case对应<when>,一旦某个case条件满足了会见break跳出,同时假设都非饱,最后还有个default可以对应这里的<otherwise>,所以最终<when>和<otherwise>中生出还只来一个碰头满足,也便只出一样起内容会为上加进去。按照上面的需,我们可以写有如下动态SQL:

<select id="selectPersonExt" parameterType="psn" resultMap="personResultMap">
    select * from person
    <where>
        <choose>
            <when test="name!=null">
                NAME = #{name}
            </when>
            <when test="gender!=null">
                GENDER = #{gender}
            </when>
            <otherwise>
                IF_IT = #{ifIT}
            </otherwise>
        </choose>
    </where>
</select>

  即可方便的贯彻该现象。

  foreach篇

  for循环大家应都非陌生,这里的foreach同样关键用来迭代集合,那么SQL中哪些地方会就此到集呢,用过in的应比熟悉,例如下面select语句:

select * from person where age in(10,20,30,40)

  上面语句可以查询有年龄为10要20或者30或者40底口,这些年纪多少是一个汇聚,但是经过参数传入的聚集是动态的,我们不容许预知数值与诸如这样描绘好,MyBatis提供的<foreach>即可实现该意义。该集合作也参数传入,以上气象方法定义及SQL语句可以形容成如下所示:

List<Person> selectForeachAge(List<Integer> ageList);

<select id="selectForeachAge" resultMap="personResultMap">
    select * from person where age in
    <foreach collection="list" item="age" index="i" open="(" close=")" separator=",">
        #{age}
    </foreach>
</select>

   这里我们得看来,<foreach>共有6单特性。

  item:表示集合迭代时元素的号,这里对应#{age}中的age

  index:集合迭代时索引,用于表示集合此时迭代及的职

  open、close、separator:这三个分级代表起始、结束、分隔符,在此处,我们用了in语句询问时应有都掌握,使用到聚集的查询语句结构要部分足描述如下所示: SELECT
column_name(s) FROM table_name
WHERE column_name IN (value1,value2,…)
。可以观看于IN关键字之背后,集合内容少于限分别是左括号、右括号,中间集的次第要素中为此逗号隔开,正好跟此的老三个属性分别对应。

  collection:这个特性是必要的,在这里我们传入的是单个参数就一个List,所以属性值就是list。

  完整示例

  上面就是是动态SQL的相继要素的中心内容,熟悉后会吃咱编辑SQL时尤其便于与活,下面为来总体代码示例供参考。

  maven工程结构要下图

  MyBatis 3

  MyBatis配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 这里可以定义类的别名,在mapper.xml文件中应用会方便很多 -->
    <typeAliases>
        <typeAlias alias="psn" type="com.mmm.pojo.Person" />
    </typeAliases>
    <!-- 环境配置 -->
    <environments default="envir">
        <environment id="envir">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://192.168.0.100:3306/ssm?characterEncoding=utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="abc123"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="com/mmm/mapper/personMapper.xml"/>
    </mappers>
</configuration>

  实体类(Person)

package com.mmm.pojo;

public class Person {
    private Integer id;            //主键
    private String name;        //姓名
    private String gender;        //性别
    private Integer age;        //年龄
    private String ifIT;        //是否从事IT行业

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getGender() {
        return gender;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public String getIfIT() {
        return ifIT;
    }
    public void setIfIT(String ifIT) {
        this.ifIT = ifIT;
    }
    @Override
    public String toString() {
        return "Person [id=" + id + ", name=" + name + ", gender=" + gender
                + ", age=" + age + ", ifIT=" + ifIT + "]";
    }

}

  Mapper接口(PersonMapper)

package com.mmm.mapper;

import java.util.List;

import com.mmm.pojo.Person;

public interface PersonMapper {

    //查找所有Person对象,返回集合类型,用于在测试类中查看动态SQL结果
    List<Person> selectAll();

    //用于测试查询语句中if、where、trim
    List<Person> selectPerson(Person p);
    List<Person> selectPerson1(Person p);
    List<Person> selectPerson2(Person p);

    //用于测试更新语句中if、set、trim
    void updatePerson(Person p);
    void updatePerson1(Person p);
    void updatePerson2(Person p);

    //用于测试choose、where、otherwise
    List<Person> selectPersonExt(Person p);

    //用于测试foreach
    List<Person> selectForeachAge(List<Integer> ageList);

}

  SQL映射文件(personMapper.xml)

<?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="com.mmm.mapper.PersonMapper">
    <resultMap type="psn" id="personResultMap">
        <id column="ID" property="id" />
        <result column="NAME" property="name" />
        <result column="GENDER" property="gender" />
        <result column="AGE" property="age" />
        <result column="IF_IT" property="ifIT" />
    </resultMap>

    <select id="selectAll" resultMap="personResultMap">
        select * from person
    </select>


    <!-- 针对查询语句中if -->
    <select id="selectPerson" parameterType="psn" resultMap="personResultMap">
        select * from person where GENDER  = '男'
            <if test="age != null">
                and AGE = #{age}
            </if>
    </select>

    <!-- 针对where -->
    <select id="selectPerson1" parameterType="psn" resultMap="personResultMap">
        select * from person
        <where>
            <if test="gender != null">
                gender = #{gender}
            </if>
        </where>
    </select>

    <!-- 针对where的 trim转换 -->
    <select id="selectPerson2" parameterType="psn" resultMap="personResultMap">
        select * from person
        <trim prefix="where" prefixOverrides="and |or " >
            <if test="gender != null">
                and gender = #{gender}
            </if>
            <if test="age != null">
                and age = #{age}
            </if>
        </trim>
        <trim prefix="" prefixOverrides="" suffix="" suffixOverrides=""></trim>
    </select>


    <!-- 针对更新语句中if -->
    <update id="updatePerson">
        update person set
        <if test="name != null">
            NAME = #{name},
        </if>
        <if test="gender != null">
            GENDER = #{gender}
        </if>
        where ID = #{id}
    </update>

    <!-- 针对set -->
    <update id="updatePerson1">
        update person
        <set>
            <if test="name != null">
                NAME = #{name},
            </if>
            <if test="gender != null">
                GENDER = #{gender}
            </if>
        </set>
    </update>

    <!-- 针对set的trim转换 -->
    <update id="updatePerson2">
        update person
        <trim prefix="set" suffixOverrides=",">
            <if test="name != null">
                NAME = #{name},
            </if>
            <if test="gender != null">
                GENDER = #{gender}
            </if>
        </trim>
    </update>

    <!-- choose when otherwise -->
    <select id="selectPersonExt" parameterType="psn" resultMap="personResultMap">
        select * from person
        <where>
            <choose>
                <when test="name!=null">
                    NAME = #{name}
                </when>
                <when test="gender!=null">
                    GENDER = #{gender}
                </when>
                <otherwise>
                    IF_IT = #{ifIT}
                </otherwise>
            </choose>
        </where>
    </select>

    <!-- foreach -->
    <select id="selectForeachAge" resultMap="personResultMap">
        select * from person where age in
        <foreach collection="list" item="age" index="i" open="(" close=")" separator=",">
            #{age}
        </foreach>
    </select>

</mapper>

  最后测试

package com.mmm.test;

import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import com.mmm.mapper.PersonMapper;
import com.mmm.pojo.Person;

public class TestDB {

    static PersonMapper mapper;

    static {
        //直接实例SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //MyBatis配置文件路径
        String path = "mybatis-config.xml";
        //通过路径获取输入流
        Reader reader = null;
        try {
            reader = Resources.getResourceAsReader(path);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //通过reader构建sessionFactory
        SqlSessionFactory sessionFactory = builder.build(reader);
        //获取SqlSession对象
        SqlSession sqlSession = sessionFactory.openSession();
        //获取Mapper实例
        mapper = sqlSession.getMapper(PersonMapper.class);
    }

    @Test
    public void testSelect() throws Exception {
        Person p = new Person();
        //p.setAge(11);
        //p.setGender("男");
        List<Person> list = mapper.selectPerson2(p);
        for(Person psn:list) {
            System.out.println(psn);
        }
    }

    @Test
    public void testUpdate() throws Exception {
        Person p = new Person();
        p.setId(10001);
        //p.setName("小改2");
        //p.setGender("男");
        mapper.updatePerson2(p);
        List<Person> list = mapper.selectAll();
        for(Person psn:list) {
            System.out.println(psn);
        }

    }

    @Test
    public void testSelectExt() throws Exception {
        Person p1 = new Person();
        //p1.setName("小红");
        //p.setGender("男");
        p1.setIfIT("是");
        List<Person> list = mapper.selectPersonExt(p1);
        for(Person psn:list) {
            System.out.println(psn);
        }

    }

    @Test
    public void testSelectForeachAge() throws Exception {
        List<Integer> ageList = new ArrayList<Integer>();
        ageList.add(21);
        ageList.add(25);
        ageList.add(36);
        List<Person> list = mapper.selectForeachAge(ageList);
        for(Person psn:list) {
            System.out.println(psn);
        }

    }
}

  小结 

  以上就是为MyBatis动态SQL的情节,在测试接近吃可以各种尝试改变各种输入值,来查阅效果,文中虽然每个元素还干到了,但是小地方还留存不足,并未过多深入扩展,例如最后之foreach,我们的参数不肯定是单科,而且为非必然是汇聚,这些情况我们都欠怎么处理,按自己之得再次错过深入学习和询问,往往很快会来深刻印象。一个题目同方式的次序问题,当遇题目后,顺着问题去探寻法后,往往深好记住。反之,没有问题以及运场景,单纯的学道以及申辩意义应会低一些。最后要还是把这些基础之事物作明白,再失去逐渐延伸。

网站地图xml地图