前言
动态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就叫listitem:将当前遍历出的元素赋值给指定的变量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里面begininsert 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 dualunionselect 'test_a_02' lastName,'test_a_e02' email from dualunionselect '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+'%'"/>SELECTtpq.nameFROMte_template_paper tpqWHEREtpq.name LIKE #{_tmpName}AND tpq.is_delete = 0 ORDER BY tpq.name desc limit 1</select>
如果不使用bind
应该是这样的
<!--根据课程名称+学年学期查试卷名称--><select id="findPaperNameByName" parameterType="String" resultType="String">SELECTtpq.nameFROMte_template_paper tpqWHEREtpq.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 注入。
