Mybatis框架学习之使用篇二:标签语法

常用标签的使用姿势小结及参数绑定的三种方式

  • select
  • update
  • delete
  • insert
  • choose, when, otherwise
  • if
  • bind
  • foreach
  • trim
  • set
  • where

I. 结构标签

xml中,一般常见的写法如下

1
2
3
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.String" >  
selext * from xxx where ...
</select>

1. 结构说明

分析上面的demo,三个参数比较重要

  • id:与dao层接口中定义的方法对应,不允许出现相同id的case
  • resultMap: 出参
  • resultType: 出参类型
  • parameterType:传参类型

2. sql片段定义

一般我们查询时,大多不建议直接使用 select * 的方式,而是具体的写上查询的列,然后一个问题就来了,每个查询都得写上一遍,不仅麻烦,对于后续表结构标定时,需要改的地方要多,所以就有了sql片段定义

  • :通过该标签可定义能复用的sql语句片段,在执行sql语句标签中直接引用即可。这样既可以提高编码效率,还能有效简化代码,提高可读性

实际case

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<sql id="poetryEntity">
id, title, author, content, `explain`, `type`, `tag`, `theme`, `is_deleted` as isDeleted,
UNIX_TIMESTAMP(`created_at`) as created,
UNIX_TIMESTAMP(`updated_at`) as updated
</sql>

<!-- 引用方式 -->
<select id="queryPoetryById" parameterType="long" resultType="com.git.hui.demo.mybatis.entity.PoetryEntity">
select
<include refid="poetryEntity"/>
from
poetry
where
id=#{id}
</select>

3. 常见标签

1
2
3
4
- <select>  表示这是一个查询sql
- <update> 更新sql
- <delete> 删除sql
- <insert> 插入sql

II. 内部标签

1. where

通常sql中,所有的where都可以被<where>替换,而这个标签的主要作用是为了防止sql可能出现的异常状况,如以下几种case

a. case1 无查询条件

1
2
3
4
select * from table where
<if test="id != null">
id = #{id}
</if>

当id不存在时,导致上面的sql被解析为 select * from table where, 显然这是一个非法sql

b. case2 最前or最后的连接条件

1
2
3
4
5
6
7
select * from table where
<if test='id != null'>
id = #{id}
</if>
<if test='uname != null'>
and uname=#{uname}
</if>

当id不存在,uname存在时,上面的sql被解析为 select * from table where and uname=#{uname, 显然也是非法sql

所以,这种场景下,用<where>标签优势就很明显了,在解析时,会根据实际的sql,来决定是否有where,是否去掉前面和后面非法的and/or

c. trim标签

除了直接使用where标签之外,更常见的一个就是标签了,

  • prefix:前缀
  • suffix:后缀
  • prefixOverrides:忽略第一个指定分隔符
  • suffixOverrides:会略最后一个分隔符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<select id="user" parameterType="user" resultType="User">
select * from user
<trim prefix="WHERE" prefixoverride="and | or">
    <if test="id!=null and id!=''">
       id=#{id}
    </if>
    <if test="name!=null and name!=''">
       and name=#{name}
    </if>
    <if test="gender!=null and gender!=''">
       and gender=#{gender}
</if>
</trim>
</select>

2. foreach

用于循环的场景,如常见的 xxx in (xxx, xxx) ,通常就是foreach来使用

  • collection:迭代的参数,一般是个列表or数组,值一般直接为参数名
  • index: 迭代过程中,元素的别称
  • open: 开始符号 如(
  • close: 结束符号,如)
  • separator:分割符号

一个实际case如下

接口为:

1
List<PoetryEntity> queryPoetryByIds(@Param("ids") List<Long> ids);

对应的sql为

1
2
3
4
5
6
7
8
9
10
11
12
<select id="queryPoetryByIds" resultType="com.git.hui.demo.mybatis.entity.PoetryEntity">
select
<include refid="poetryEntity"/>
from
poetry
where
id IN
<foreach collection="ids" index="index" item="id" open="(" separator="," close=")">
#{id}
</foreach>
limit 500
</select>

3. Choose

选择标签,类似java中的switch,一旦其中一个条件匹配,整个choose块结束

一个xml如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<select id="getUserList_choose" resultMap="resultMap_user" parameterType="com.yiibai.pojo.User">  
SELECT *
FROM User u
<where>
<choose>
<when test="username !=null ">
u.username LIKE CONCAT(CONCAT('%', #{username, jdbcType=VARCHAR}),'%')
</when >
<when test="sex != null and sex != '' ">
AND u.sex = #{sex, jdbcType=INTEGER}
</when >
<when test="birthday != null ">
AND u.birthday = #{birthday, jdbcType=DATE}
</when >
<otherwise>
limit 10
</otherwise>
</choose>
</where>
</select>

mybatis的这个选择,基本上和我们的switch语句一样, 对应关系如下

1
2
3
4
5
6
7
<choose>                 ---- switch

<when></when> ---- case cond1
<when></when> ---- case cond2

<otherwise></otherwise> ---- default
</choose>

4. if

条件判断,在拼装sql时,最常见的就是为了防止传入null的参数,导致拼出一个业务逻辑有问题的sql,用if标签就很有用了

使用姿势也比较简单了,主要是内部的test条件判断

1
2
3
<if test="sex != null and sex != ''"> 
and sex=#{sex}
</if>

5. set标签

更新的时候使用,同样是为了解决拼装成的sql中,最前面or最后面的英文逗号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<update id="userUpdate" parameterType="user">
  update user
  <set>
   <if test="id!=null and id!=''">
   id=#{id},
   </if>
   <if test="name!=null and name!=''">
    name=#{name},
</if>
   <if test="gender!=null and gender!=''">
    gender=#{gender},
   </if>
  </set>
</update>

6. bind

bind 元素可以从 OGNL 表达式中创建一个变量并将其绑定到上下文

比如对于like查询,需要在参数的前面or后面加上 %, 就可以这么玩:

1
2
3
4
5
6
7
8
9
<select id="queryPoetryByContent" resultType="com.git.hui.demo.mybatis.entity.PoetryEntity">
<bind name="pattern" value="'%' + content + '%'" />
select
<include refid="poetryEntity"/>
from
poetry
where content LIKE #{pattern}
limit 10
</select>

上面的也可以使用concat来做,如下

1
2
3
4
5
6
7
8
<select id="queryPoetryByContent" resultType="com.git.hui.demo.mybatis.entity.PoetryEntity">
select
<include refid="poetryEntity"/>
from
poetry
where content LIKE CONCAT(CONCAT('%', #{content}), '%')
limit 10
</select>

III. 参数替换

dao层的参数,是如何传入到xml中的sql语句的呢?

1. map传递

dao层接口参数为map,xml中可以直接通过map中的key,来绑定参数

1
public User selectUser(Map paramMap);

假设传入的参数为两个,uname, password

对应的xml为

1
2
3
<select id=" selectUser" resultMap="BaseResultMap">
select * from users where uname = #{uname,jdbcType=VARCHAR} and password=#{password,jdbcType=VARCHAR}
</select>

2. 参数位置替换

这种是直接根据参数的索引位置来绑定, {0} 表示第一个参数, {1} 表示第二个参数

1
public User selectUser(String uname, String password);

对应的xml

1
2
3
<select id=" selectUser" resultMap="BaseResultMap">
select * from users where uname = #{0} and password=#{1}
</select>

3. 注解指定方式

通过 @Param注解,直接指定name,在sql中即可通过name方式引用

1
public User selectUser(@Param("uname") String uname, @Param("password") String password);

对应的sql为

1
2
3
<select id=" selectUser" resultMap="BaseResultMap">
select * from users where uname = #{uname,jdbcType=VARCHAR} and password=#{password,jdbcType=VARCHAR}
</select>

4. $#区别

使用#传入参数是,sql语句解析是会加上””,

比如 select * from table where name = #{name} ,传入的name为小李,那么最后打印出来的就是

select * from table where name = "小李",就是会当成字符串来解析

因此在动态排序时,比如 order by column,这个时候务必要用${},因为如果你使用了#{}

区别

  • #将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号
  • $将传入的数据直接显示生成在sql中
  • #方式能够很大程度防止sql注入
  • $方式无法防止Sql注入
  • $方式一般用于传入数据库对象,例如传入表名,列名

简单来说,两者区别: $ 是sql替换,直接拼成一条可执行sql; # 是参数替换

IV. 其他

项目工程

个人博客: 一灰灰Blog

基于hexo + github pages搭建的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

声明

尽信书则不如,已上内容,纯属一家之言,因本人能力一般,见识有限,如发现bug或者有更好的建议,随时欢迎批评指正

扫描关注

QrCode