本篇内容包括:映射配置 Demo、 select 语句属性详解、resultType&resultMap 注解的使用、 #{} 与 ${} 的区别、动态 SQL 相关标签(if、where、set等)以及MyBatis 映射关联查询(一对多、多对一、多对多)。
MyBatis 的真正强大在于它的语句映射,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 致力于减少使用成本,让用户能更专注于 SQL 代码。
映射配置文件(mapper文件)中包含了要执行的 SQL 语句以及相应的数据和对象之间的映射关系。
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="StudentMapper">
<select id="selectAll" resultType="student">
SELECT *
FROM student
select>
<select id="selectById" resultType="com.bean.student" parameterType="int">
SELECT *
FROM student
WHERE id = #{id}
select>
<insert id="insert" parameterType="student">
INSERT INTO student
VALUES (#{id}, #{name}, #{age})
insert>
<update id="update" parameterType="student">
UPDATE student
SET name = #{name},
age = #{age}
WHERE id = #{id}
update>
<delete id="delete" parameterType="int">
DELETE
FROM student
WHERE id = #{id}
delete>
mapper>
select 语句有很多属性:
resultType 可表示返回基本类型,也可表示返回自定义对象类型
返回基本类型:
<select id="listUserInfo" resultType="int">
SELECT userId FROM user_info
select>
返回自定义类型:
<select id="getUserInfoById" resultType="UserInfo">
SELECT user_name ,user_addr FROM user_info WHERE user_id=#{user_id}
select>
resultMap 主要解决 TABLE 字段与 JavaBean 映射不匹配问题。
举个栗子:
定义一个 JavaBean:
@Getter
@Setter
public class UserInfo {
String userName;
String addr;
}
JavaBean 对应的 sql 语句:
DROP TABLE IF EXISTS `user_info`;
CREATE TABLE `user_info` (
`user_id` int(5) NOT NULL AUTO_INCREMENT,
`user_name` varchar(50) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
`user_addr` varchar(100) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
PRIMARY KEY (`user_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 16 CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Compact;
SET FOREIGN_KEY_CHECKS = 1;
JavaBean 与 Sql 语句中,user_name 与 userName 不匹配,user_addr 与 addr 不匹配。
通过 resultMap 就能很好地解决该问题
<resultMap id="userInfoMap" type="UserInfo">
<result property="userName" column="user_name"/>
<result property="addr" column="user_addr"/>
resultMap>
property 表示 JavaBean 属性,column 表示 table 表字段。
在映射文件的 Sql 语句中 #{} 表示占位符,相当于 ‘?’,${} 需要经过预处理,能防止SQL漏洞注入。
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。
动态 SQL 的原理是:使用 OGNL 从 sql 参数对象中计算表达式的值,根据表达式的值动态拼接 sql,以此来完成动态 sql 的功能。
MyBatis 中的动态 SQL 标签主要包括:
元素 | 作用 | 备注 |
---|---|---|
if | 判断语句 | 单条件分支判断 |
choose(when、otherwise) | 相当于 Java 中的 switch case 语句 | 多条件分支判断 |
trim,where | 辅助元素 | 用于处理一些SQL拼装问题 |
foreach | 循环语句 | 在in语句等列举条件常用 |
bind | 辅助元素 | 拼接参数 |
if 语句使用方法简单,常常与 test 属性联合使用:
<if test="判断条件"> SQL语句 if>
普通的 Sql 中对于只有一个参数,后面的 #{param} 表示占位符,里面的 param 可以为任意值,对于多个参数则须写清对应的 pojo 类里面的属性
<select id="selectUserByUsernameAndSex" resultType="User" parameterType="User">
select * from user where username=#{username} and sex=#{sex}
select>
在 普通的 Sql 中如果我们想 #{username} 为空情况下就只查 #{sex},这种情况该如何实现呢?使用 if 来判断,可多个 if 语句同时使用。以下语句表示为可以按照网站名称(name)或者网址(url)进行模糊查询。如果您不输入名称或网址,则返回所有的网站记录。但是,如果你传递了任意一个参数,它就会返回与给定参数相匹配的记录。
<select id="selectAllWebsite" resultMap="myResult">
SELECT id,name,url
FROM website
WHERE 1=1
<if test="name != null">
AND name like #{name}
if>
<if test="url!= null">
AND url like #{url}
if>
select>
where、if 同时使用可以进行查询、模糊查询
SELECT
*
FROM
collection_cost_detail
AND collection_bill_main_id = #{collectionBillMainId}
AND collection_sub_bill_type = #{collectionSubBillType}
AND cost_type = #{costType}
Ps:
这个
Set、if 同时使用可以用来修改
<update id="upd">
update student
<set>
<if test="sname != null">sname=#{sname},</if>
<if test="spwd != null">spwd=#{spwd},</if>
<if test="sex != null">sex=#{sex},</if>
<if test="phone != null">phone=#{phone}</if>
sid=#{sid}
</set>
where sid=#{sid}
</update>
有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句
<select id="selectUserByChoose" resultType="com.ys.po.User" parameterType="com.ys.po.User">
select * from user
<where>
<choose>
<when test="id !='' and id != null">
id=#{id}
when>
<when test="username !='' and username != null">
and username=#{username}
when>
<otherwise>
and sex=#{sex}
otherwise>
choose>
where>
select>
在上面代码中,我们有三个条件,id,username,sex,只能选择一个作为查询条件
select * from user where id=?;
select * from user where username=?;
select * from user where sex=?;
trim 标记是一个格式化的标记,可以完成 set 或者是 where 标记的功能
用 trim 改写上面第二点的 if+where 语句:
<select id="selectUserByUsernameAndSex" resultType="user" parameterType="com.ys.po.User">
select * from user
<trim prefix="where" prefixOverrides="and">
<if test="username != null">
and username=#{username}
if>
<if test="sex != null">
and sex=#{sex}
if>
trim>
select>
用 trim 改写上面第三点的 if+set 语句:
<update id="updateUserById" parameterType="com.ys.po.User">
update user u
<trim prefix="set" suffixOverrides=",">
<if test="username != null and username != ''">
u.username = #{username},
if>
<if test="sex != null and sex != ''">
u.sex = #{sex},
if>
trim>
where id=#{id}
update>
trim+if 同时使用可以添加:
<insert id="add">
insert into student
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="sname != null">sname,if>
<if test="spwd != null">spwd,if>
<if test="sex != null">sex,if>
<if test="phone != null">phone,if>
trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="sname != null">#{sname},if>
<if test="spwd != null">#{spwd},if>
<if test="sex != null">#{sex},if>
<if test="phone != null">#{phone}if>
trim>
insert>
foreach是用来对集合的遍历,这个和 Java 中的功能很类似。通常处理 Sql 中的 in 语句。
//批量查询
<select id="findAll" resultType="Student" parameterType="Integer">
<include refid="selectvp"/> WHERE sid in
<foreach item="ids" collection="array" open="(" separator="," close=")">
#{ids}
foreach>
select>
//批量删除
<delete id="del" parameterType="Integer">
DELETE FROM student where sid in
<foreach item="ids" collection="array" open="(" separator="," close=")">
#{ids}
foreach>
delete>
foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符
你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
在实际开发中会遇到许多相同的SQL,比如根据某个条件筛选,这个筛选很多地方都能用到,我们可以将其抽取出来成为一个公用的部分,这样修改也方便,一旦出现了错误,只需要改这一处便能处处生效了,此时就用到了
举个栗子:
<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.addr sql>
引定义好的 Sql:
<select id="listUserInfo" resultMap="userInfoMap">
SELECT
<include refid="userColumns">
<property name="alias" value="t1"/>
include>
FROM user_info t1
select>
<resultMap id="myStudent1" type="student1">
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
<result property="sex" column="sex"/>
<result property="sage" column="sage"/>
<collection property="list" ofType="teacher">
<id property="tid" column="tid"/>
<result property="tname" column="tname"/>
<result property="tage" column="tage"/>
collection>
resultMap>
<select id="find1" resultMap="myStudent1">
select *
from student1 s
left join teacher t on s.sid = t.sid
select>
<resultMap id="myTeacher" type="teacher">
<id property="tid" column="tid"/>
<result property="tname" column="tname"/>
<result property="tage" column="tage"/>
<association property="student1" javaType="Student1">
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
<result property="sex" column="sex"/>
<result property="sage" column="sage"/>
association>
resultMap>
<select id="find2" resultMap="myTeacher">
select *
from teacher t
right join student1 s on t.sid = s.sid
select>
<select id="find3" resultMap="myStudent1">
select *
from student1 s
left join relevance r on s.sid = r.sid
left join teacher t on r.tid = t.tid
select>