前言
动态sql是mybatis强大特性之一,简化了拼装sql的操作,大量的判断都可以在mybatis的映射xml文件里面配置,打到原本需要大量代码才能实现的功能,大大减少了编写代码的工作量,体现了mybatis的灵活性,高度可配置性和可维护性
元素
mybatis的动态sql包含以下几个元素
下面来讨论一些这些元素的用法
if
常用的判断语句,相当于java中的if语句,常常与test属性联合使用
这么一个需求:我们需要根据lastName查询员工信息,但是lastName是一个可填可不填的条件,不填写的时候就不要用它作为查询条件了.
select * from tbl_employee
<!-- test:判断表达式(OGNL)
OGNL参照PPT或者官方文档。
c:if test
从参数中取值进行判断
遇见特殊符号应该去写转义字符:
&&:
-->
where
<if test="lastName!=null && lastName!=""">
and last_name like #{lastName}
</if>
这样就会有一个问题啊,如果lastname有值,那么sql就会多了一个and,那么这样有两个解决方案
1.给where后面加上1=1,以后的条件都and xxx
2.mybatis使用where标签来将所有的查询条件包括在内,mybatis就会将where标签中拼装的sql,多出来的and或者or去掉. 注意: where只会去掉第一个多出来的and或者or
eg:
select * from tbl_employee
<!-- where -->
<where>
<if test="lastName!=null && lastName!=""">
and last_name like #{lastName}
</if>
</where>
choose
select * from tbl_employee
<where>
<!-- 如果带了id就用id查,如果带了lastName就用lastName查;只会进入其中一个 -->
<choose>
<when test="id!=null">
id=#{id}
</when>
<when test="lastName!=null">
last_name like #{lastName}
</when>
<when test="email!=null">
email = #{email}
</when>
<otherwise>
gender = 0
</otherwise>
</choose>
</where>
trim
我们在讲if的时候, 最后讲到了,为了去掉多余的and或者or,添加了where标签.但是where只会去掉第一个多出来的and或者or. 如果现在是在后面多出的and或者or, where标签就不能解决了. 引入新的标签trim
trim意味着我们需要去掉一些特殊的字符串
prefix代表的语句的前缀,给拼串后的整个字符串加上一个前缀
prefixOverrides代表的是需要去掉的那种字符串,去掉整个字符串前面多余的字符
suffix:后缀,给拼串后的整个字符串加上一个后缀
suffixOverrides:后缀覆盖,去掉整个字符串后面多余的字符
select * from tbl_employee
<!-- 自定义字符串的截取规则 -->
<trim prefix="where" suffixOverrides="and">
<if test="lastName!=null && lastName!=""">
last_name like #{lastName} and
</if>
</trim>
在hibernate中如果想更新某个字段,是需要发送所有的字段给持久对象的.但是这样对网络宽带消耗很大呀.而在mybatis中,就可以使用set来完成这些功能
set
<!-- 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}
set元素遇到了逗号,它会把对应的逗号去掉
如果不用set标签,那就转变成对应的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}
foreach
<!--public List<Employee> getEmpsByConditionForeach(List<Integer> ids); -->
<select id="getEmpsByConditionForeach" resultType="com.kwy.mybatis.bean.Employee">
select * from tbl_employee
<!--
collection:指定要遍历的集合:
list类型的参数会特殊处理封装在map中,map的key就叫list
item:将当前遍历出的元素赋值给指定的变量
separator:每个元素之间的分隔符
open:遍历出所有结果拼接一个开始的字符
close:遍历出所有结果拼接一个结束的字符
index:索引。遍历list的时候是index就是索引,item就是当前值
遍历map的时候index表示的就是map的key,item就是map的值
#{变量名}就能取出变量的值也就是当前遍历出的元素
-->
<foreach collection="ids" item="item_id" separator=","
open="where id in(" close=")">
#{item_id}
</foreach>
</select>
保存的时候,就会有批量保存,可以foreach遍历,mysql支持values(),(),()语法
eg1:
<insert id="addEmps">
insert into tbl_employee(
<!-- 引用外部定义的sql -->
<include refid="insertColumn"></include>
)
values
<foreach collection="emps" item="emp" separator=",">
(#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id})
</foreach>
</insert>
<!--
抽取可重用的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>
eg2:
<!-- 这种方式需要数据库连接属性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>
讲完了mysql,我们来讲一下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;
sql:
<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>
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
)
sql:
<!-- oracle第二种批量方式 -->
insert into employees(employee_id,last_name,email)
<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>
内置参数
两个内置参数:
不只是方法传递过来的参数可以被用来判断,取值。。。 mybatis默认还有两个内置参数: _parameter:代表整个参数 单个参数:_parameter就是这个参数 多个参数:参数会被封装为一个map;_parameter就是代表这个map _databaseId:如果配置了databaseIdProvider标签。 _databaseId就是代表当前数据库的别名oracle
bind,可以将gnl表达式的值绑定到一个变量中,方便后来引用这个变量的值
eg:
<!--根据课程名称+学年学期查试卷名称-->
<select id="findPaperNameByName" parameterType="String" resultType="String">
<bind name="_tmpName" value="'%'+tmpName+'%'"/>
SELECT
tpq.name
FROM
te_template_paper tpq
WHERE
tpq.name LIKE #{_tmpName}
AND tpq.is_delete = 0 ORDER BY tpq.name desc limit 1
</select>
如果不使用bind
应该是这样的
<!--根据课程名称+学年学期查试卷名称-->
<select id="findPaperNameByName" parameterType="String" resultType="String">
SELECT
tpq.name
FROM
te_template_paper tpq
WHERE
tpq.name LIKE concat('%'+tmpName +'%')
AND tpq.is_delete = 0 ORDER BY tpq.name desc limit 1
</select>
使用concat函数连接字符串,在mysql中,这个函数是支持多个函数的,但是oracle只支持两个参数,由于不同数据库之间的语法差异,如果更换数据库,有些sql语句可能就需要重写,针对这种情况,使用bind标签就可以避免了.
bind 标签的两个属性都是必选项, name 为绑定到上下文的变量名, va l ue 为 OGNL 表 达式。创建一个 bind 标签的变量后 , 就可以在下面直接使用,使用 bind 拼接字符串不仅可 以避免因更换数据库而修改 SQL,也能预防 SQL 注入。