MyBatis

MyBati学习总结(六)

动态SQL

动态SQL能够根据输入的参数而确定最终提交给数据库的SQL语句。当传入的参数满足一定的条件时,MyBatis就会相应地拼接对应的语句,且能够自动地处理空格、逗号和语句头部的连词。

动态SQL需要用到if、where、foreach等标签来实现。

if标签

if标签时动态SQL的灵魂,分支条件全都要if标签的test属性来实现。如果test当中的语句结果为真,就将这个if标签中的sql语句拼接到总的sql语句中:

<select id="queryPerosnByNameOrAgeWithSQLTag" parameterType="person" resultType="person">
        select id,name,age from person where 
        <if test="name!=null and name!=''">
            name=#{name}
        </if>
        <if test="age!=null and age!=0">
            and age=#{age}
        </if>

    </select>
Person person=new Person();
        person.setName("刘咯咯");
        person.setAge(19);
        Person pers=personMapper.queryPerosnByNameOrAgeWithSQLTag(person);

where标签

if标签是按照代码的顺序执行的,如果第一个条件不成立,且有第二个条件成立的话,由于第二个if标签中的sql语句首部必然有连接词and或or,那么将其与原sql语句拼接之后必然会导致sql语句解析错误。

可以巧妙地在where后面加一句1=1,就可以暂时解决这个问题了:

    <select id="queryPerosnByNameOrAgeWithSQLTag" parameterType="person" resultType="person">
        select id,name,age from person where 1=1
        <if test="name!=null and name!=''">
            and name=#{name}
        </if>
        <if test="age!=null and age!=0">
            and age=#{age}
        </if>

    </select>

但是还是有一个问题,1=1对于or来讲就是恒真了,会屏蔽所有or后i按的语句,你说加一句1<>1吧,又防不了and语句,所以MyBatis推出了更加灵活的where标签来处理这问题:

<select id="queryPerosnByNameOrAgeWithSQLTag" parameterType="person" resultType="person">
select id,name,age from person
<where>
<if test="name!=null and name!=''">
and name=#{name}
</if>
<if test="age!=null and age!=0">
and age=#{age}
</if>
</where>
</select>

将所有的if标签都写入where标签内,where语句会处理第一个成立的if标签中的and(或者or)。这个时候哪怕第一个if中写上了and或test不通过,也能够拼接成正确的语句。不过where只会处理第一个成立的if标签,如果之后的标签中缺少连接词的话研究会报错。

foreach标签

foreach标签是为了让动态SQL能够处理in语句中的内容,例如查询编号为1、2、13的人的信息:

select id, name from person where id in(1,2,13)

我们想让in后面的那个集合能用动态SQL的方式动态传入。显然多个元素的话需要遍历每一个元素来查询,如果企图直接通过读取属性的方式是行不通的,例如定义一个Country类,其中有一个成员变量list为ids,在下面的语句中,会报无法识别ids的错误:

<select id="queryPersonWithIdInCountry" parameterType="Country" resultType="person">
        select * from person
        <where>
            <if test="ids!=null and ids.size>0">
                and id in(ids)
            </if>
        </where>

</select>

foreach看名字就知道是实现迭代的功能。foreach可以迭代的类型有:属性、数组(包括对象数组)、集合等。

属性

还是以上面的那个country中的属性ids为例,想要用foreach处理它,实现起来就和java中的增强for差不多:

<select id="queryPersonWithIdInCountry" parameterType="Country" resultType="person">
select * from person
<where>
<if test="ids!=null and ids.size>0">
<foreach collection="ids" open=" and id in(" close=")" item="id" separator=",">
#{id}
</foreach>

</if>
</where>
</select>

collection中写属性名,item中写入每一次遍历取出的值(相当于for (String name: [names])中的name);由于一个sql语句中不止要遍历的集合,还包括语句中的其它部分,所以用open写入要迭代的元素的前面的内容,用close写入后面的内容。最后还需要定义分隔符,用separator来写。

foreach标签内写入展位符,表示要迭代的部分的取值。所以整个sql语句就被拆成了这样:

select * from person【open】and id in(【item】#{id}【separator】,【item】#{id}【close】)

简单类型的数组

当传入的是简单类型的数组时,不仅输入参数的类型要改为对应的数组类型,而且在使用时需要用array来代替所有的数组名。原因时数组名时作为形参传入进来的,无法写成映射。

<select id="queryPersonWithArray" parameterType="int[]" resultType="person">
select * from person
<where>
<if test="array!=null and array.length>0">
<foreach collection="array" open=" and id in(" close=")" item="id" separator=",">
#{id}
</foreach>
</if>
</where>
</select>

集合类型(list)

和传入简单类型的数组差不多,将传入的参数写为list,且将所有集合名写为list:

<select id="queryPersonWithList" parameterType="list" resultType="person">
select * from person
<where>
<if test="list!=null and list.size>0">
<foreach collection="list" open=" and id in(" close=")" item="id" separator=",">
#{id}
</foreach>

</if>
</where>
</select>

对象数组

假设要传入一个person类的数组:

Person per1=new Person();
per1.setId(1);
Person per2=new Person();
per2.setId(2);
Person per13=new Person();
per13.setId(13);

Person persons[]=new Person[]{per1,per2,per13};

现在传入的类型并不能写Person[]了,会报错无法识别这个别名,只能传入Object[](我觉得挺迷的,为什么parameterType写单个Person类就可以写Person数组就不行了)。

<select id="queryPersonWithObjectArray" parameterType="Object[]" resultType="person">
    select * from person
    <where>
      <if test="array!=null and array.length>0">
          <foreach collection="array" open=" and id in(" close=")" item="person" separator=",">
             #{person.id}
          </foreach>
      </if>
    </where>
</select>

现在每一次迭代取出的都是一个person实例,真正要用的其实时person实例中的id变量,所以在占位符中要写item中的临时变量(person)点id。

SQL片段

对于需要重复出现很多次的代码,在java中可以将其写入一个方法中,在SQL里可以写入存储过程或存储函数中。那么在MyBatis中,可以使用SQL片段处理可能需要多次出现的SQL语句。

第一步要提取相似代码。 在Mapper.xml文件中的namespace标签下写sql标签(与增删改查的标签同级),在sql标签中写入需要提取的代码:

<sql id="queryWithObjectArray">
<where>
<if test="array!=null and array.length>0">
<foreach collection="array" open=" and id in(" close=")" item="person" separator=",">
#{person.id}
</foreach>
</if>
</where>
</sql>

sql标签的id属性作用与增删改查的id相同,唯一标识这个SQL片段。

第二步引用。在需要引用的地方写入refield标签,补充完id值之后就可以引用完成了:

<select id="queryPersonWithObjectArray" parameterType="Object[]" resultType="person">
select * from person
<include refid="queryWithObjectArray"></include>
</select>

如果引用的地方不在SQL片段同一个文件,需要在引用时加上namespace。

发表评论