动态SQL
if标签
一般应用在多条件查询中
xml
<select id="selectByMultipleCondition" resultType="car" parameterType="Car">
SELECT id, car_num AS carNum, brand, guide_price AS guidePrice, produce_time AS produceTime, car_type AS carType
FROM t_car WHERE 1=1
/* if标签中test属性是必须的,test属性的值是一个boolean类型的值。
只有test属性的值为true时,if标签中的SQL片段才会被拼接到最终的SQL语句中。
test属性中可以使用的是:
当使用了@Param注解:test使用的是注解指定的参数名。比如@Param("brand"),那么只能使用brand;
若没有使用@Param注解:test使用的是arg0、arg1、param1、param2
当使用了Pojo:那么test中使用的Pojo类的属性名*/
<if test="carNum != null and carNum != ''">
AND car_num = #{carNum}
</if>
<if test="brand != null and brand != ''">
AND brand = #{brand}
</if>
<if test="carType != null and carType != ''">
AND car_type = #{carType}
</if>
</select>
where标签
where标签的作用:让where子句更加动态智能,
- 所有条件都为空时,where标签保证不会生成where子句
- 自动去除某些条件前面多余的and或or(只能处理前面的,不能处理后面的)
xml
<select id="selectByMultipleConditionWithWhere" resultType="car" parameterType="Car">
SELECT id, car_num AS carNum, brand, guide_price AS guidePrice, produce_time AS produceTime, car_type AS carType
FROM t_car
<where>
<if test="carNum != null and carNum != ''">
AND car_num = #{carNum}
</if>
<if test="brand != null and brand != ''">
AND brand = #{brand}
</if>
<if test="carType != null and carType != ''">
AND car_type = #{carType}
</if>
</where>
</select>
trim标签
xml
<select id="selectByMultipleConditionWithTrim" resultType="car" parameterType="Car">
SELECT id, car_num AS carNum, brand, guide_price AS guidePrice, produce_time AS produceTime, car_type AS carType
FROM t_car
/* prefix:加前缀 suffix:加后缀 prefixOverrides:去掉前缀 suffixOverrides:去掉后缀
prefix="WHERE" 表示在trim标签所有你内容的前面加上WHERE关键字。
suffixOverrides="AND |OR " 表示如果trim标签中的内容以AND或者OR结尾,那么就去掉这个AND或者OR。
*/
<trim prefix="WHERE" suffixOverrides="AND |OR ">
<if test="carNum != null and carNum != ''">
car_num = #{carNum} AND
</if>
<if test="brand != null and brand != ''">
brand = #{brand} AND
</if>
<if test="carType != null and carType != ''">
car_type = #{carType}
</if>
</trim>
</select>
set标签
主要使用在update语句当中,用来生成set关键字,同时去掉最后多余的","
比如我们只更新提交不为空的字段,如果提交的数据是空或者"",那么不更新这个字段。
xml
<update id="updateBySet" parameterType="Car">
UPDATE t_car
<set>
<if test="carNum != null and carNum != ''">
car_num = #{carNum},
</if>
<if test="brand != null and brand != ''">
brand = #{brand},
</if>
<if test="guidePrice != null">
guide_price = #{guidePrice},
</if>
<if test="produceTime != null">
produce_time = #{produceTime},
</if>
<if test="carType != null and carType != ''">
car_type = #{carType}
</if>
</set>
WHERE id = #{id}
</update>
choose when otherwise 标签
这三个标签等同于 if... else if ... else ... 只有一个分支会被执行。
xml
<select id="selectByChoose" resultMap="car">
select * from t_car
<where>
<choose>
<when test="carNum != null and carNum != ''">
car_num = #{carNum}
</when>
<when test="brand != null and brand != ''">
brand = #{brand}
</when>
<otherwise>
1=1
</otherwise>
</choose>
</where>
</select>
foreach标签
循环数组或集合时使用
批量删除
xml
<!--对应的接口方法: int deleteByIds(@Param("ids") Long[] ids);-->
<delete id="deleteByIds" >
DELETE FROM t_car WHERE id IN
/*collection:指定要遍历的集合,可以是list、set、array等。
item:代表数组或集合中的元素
open:指定foreach标签生成的SQL片段的开头
separator:指定foreach标签生成的SQL片段之间的分隔符
close:指定foreach标签生成的SQL片段的结尾
这个foreach标签会将传入的list集合中的每个元素都用#{id}占位符替换,并且用逗号分隔开来,最终生成一个类似于(id1,id2,id3)这样的SQL片段。
这样就可以实现批量删除的功能了。
注意:在使用foreach标签时,传入的参数类型必须是一个集合类型,否则会报错。*/
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
批量插入
xml
<!--对应的接口方法: int insertBatch(@Param("list") List<Car> carList);-->
<insert id="insertBatch" parameterType="list">
INSERT INTO t_car (id, car_num, brand,guide_price,produce_time,car_type)
VALUES
<foreach collection="list" item="car" separator=",">
(null, #{car.carNum}, #{car.brand},#{car.guidePrice},#{car.produceTime},#{car.carType})
</foreach>
</insert>
sql标签和include标签
sql标签用来声明sql片段。
include标签用来将声明的sql片段包含到某个sql语句当中。
主要为了代码复用。
xml
<sql id="carColumns">
id, car_num AS carNum, brand, guide_price AS guidePrice, produce_time AS produceTime, car_type AS carType
</sql>
<select id="selectAllCarWithSql" resultType="car">
SELECT
<include refid="carColumns"/>
FROM t_car
</select>
MyBatis的高级映射及延迟加载
有一个业务场景,有学生表和班级表。一个学生对应一个班级,一个班级包含多个学生。那么学生与班级的关系就是多对一。多的一方是Student 、一的一方是Clazz。
怎么分主表和子表?
原则:谁在前谁就是主表。
多对一:多在前,多就是主表
一对多:一在前,一就是主表
多对一
多对一的映射方式包括三种:
- 一条sql语句,级联属性映射
- 一条sql语句,association
- 两条sql语句,分步查询(优点1.可复用2.支持懒加载)
第一种方式:级联属性映射
Student类设计如下,添加一个班级类的属性,表示学生关联的班级对象。
java
public class Student {
private Integer sid;
private String sname;
private Clazz clazz; // 班级对象
@Override
public String toString() {
return "Student{" +
"sid=" + sid +
", sname='" + sname + '\'' +
", clazz=" + clazz +
'}';
}
public Clazz getClazz() {
return clazz;
}
public void setClazz(Clazz clazz) {
this.clazz = clazz;
}
public Student(Integer sid, String sname) {
this.sid = sid;
this.sname = sname;
}
public Student() {
}
public Integer getSid() {
return sid;
}
public void setSid(Integer sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
}
StudentMapper.xml设计如下:
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ali.mapper.StudentMapper">
<!--一条sql语句,级联属性映射-->
<resultMap id="studentResultMap" type="Student">
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
<result property="clazz.cid" column="cid"/>
<result property="clazz.cname" column="cname"/>
</resultMap>
<!--对应的Mapper接口方法是:Student getStudentById(int id);-->
<select id="getStudentById" resultMap="studentResultMap">
select s.sid, s.sname, c.cid, c.cname
from t_student s
left join t_clazz c on s.cid = c.cid
where s.sid = #{sid}
</select>
</mapper>
第二种方式:一条sql语句,association
Pojo类不用修改,只是resultMap内容做了修改,StudentMapper.xml设计如下:
xml
<resultMap id="studentResultMapAssociation" type="Student">
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
<!--association标签表示一对一的关系,
property属性表示要映射的Pojo类的属性名,(Stusrnt类的clazz属性)
javaType属性表示这个属性的类型,
column属性表示要使用哪个列的值来查询关联对象-->
<association property="clazz" javaType="Clazz">
<id property="cid" column="cid"/>
<result property="cname" column="cname"/>
</association>
</resultMap>
<!--对应的Mapper接口方法是:Student getStudentById(int id);-->
<select id="getStudentById" resultMap="studentResultMapAssociation">
select s.sid, s.sname, c.cid, c.cname
from t_student s
left join t_clazz c on s.cid = c.cid
where s.sid = #{sid}
</select>
第三种方式:两条sql语句,分步查询
第一步查询,StudentMapper.xml设计如下:
xml
<resultMap id="studentResultMapStep" type="Student">
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
<!--association标签表示一对一的关系,
property属性表示要映射的Pojo类的属性名,(Stusrnt类的clazz属性)
column属性表示要使用哪个列的值来查询关联对象
select指定第二步sql语句的id-->
<association property="clazz" column="cid"
select="com.ali.mapper.ClazzMapper.getClazzByIdStep2"/>
</resultMap>
<select id="getStudentByIdStep1" resultMap="studentResultMapStep">
select s.sid, s.sname, s.cid
from t_student s
where s.sid = #{sid}
</select>
第二步查询,ClazzMapper.xml设计如下:
xml
<!--分步查询第二步:根据cid获取班级信息-->
<select id="getClazzByIdStep2" resultType="Clazz">
select cid, cname
from t_clazz
where cid = #{cid}
</select>
这种方式执行了2次sql查询。
多对一延迟加载
分步查询的优点:可复用性强。
延迟加载的核心就是:用的时候再查询,不用的时候不查询。
作用是提高性能。尽可能的不查或者少查。
当查询返回student对象时,此还没有执行第二步查询,当调用student.getClazz()方法时,才执行第二步查询。
在mybatis当中,怎么开启延迟加载?
在association标签种添加fetchType="lazy",这是一种局部设置,只针对当前的association关联的sql语句起作用。
那怎么开启全局的懒加载呢?
在mybatis-config.xml文件中设置如下:
xml
<settings>
<!--延迟加载的全局开关,默认关闭。
所有的分步查询,都采用延迟加载。
实际开发中建议开启。-->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
在开启全局延迟加载的情况下,如果某个分步查询不需要开启延迟加载怎么办?
在对应的association标签中添加fetchType="eager",就可以关闭这个查询的延迟加载。
一对多
一个班级对应多个学生
一对多的映射实现方式有两种:
- collection
- 分步查询
第一种方式:collection
班级类设计如下:
java
public class Clazz {
private Integer cid;
private String cname;
private List<Student> students; // 一个班级有多个学生
@Override
public String toString() {
return "Clazz{" +
"cid=" + cid +
", cname='" + cname + '\'' +
", students=" + students +
'}';
}
public List<Student> getStudents() {
return students;
}
public void setStudents(List<Student> students) {
this.students = students;
}
public Clazz() {
}
public Clazz(Integer cid, String cname) {
this.cid = cid;
this.cname = cname;
}
public Integer getCid() {
return cid;
}
public void setCid(Integer cid) {
this.cid = cid;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
}
ClazzMapper.xml文件设计如下:
xml
<resultMap id="clazzResultMap" type="Clazz">
<id property="cid" column="cid"/>
<result property="cname" column="cname"/>
<!--collection标签表示一对多的关系,
property属性表示要映射的Pojo类的属性名,(Clazz类的students属性)
ofType属性表示集合当中的元素类型,-->
<collection property="students" ofType="Student" >
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
</collection>
</resultMap>
<select id="getClazzByCollection" resultMap="clazzResultMap">
select cid, cname,s.sid, s.sname
from t_clazz c
left join t_student s on c.cid = s.cid
where c.cid = #{cid}
</select>
第二种方式:分步查询
第一步查询:ClazzMapper.xml文件设计如下:
xml
<resultMap id="clazzResultMapStep" type="Clazz">
<id property="cid" column="cid"/>
<result property="cname" column="cname"/>
<!--collection标签表示一对多的关系,
property属性表示要映射的Pojo类的属性名,(Clazz类的students属性)
ofType属性表示集合当中的元素类型,
column="cid" 表示根据cid查询学生信息-->
<collection property="students" ofType="Student" column="cid"
select="com.ali.mapper.StudentMapper.getStudentsByClazzId"/>
</resultMap>
<select id="getClazzByIdStep1" resultMap="clazzResultMapStep">
select cid, cname
from t_clazz
where cid = #{cid}
</select>
第二步查询:StudentMapper.xml设计如下:
xml
<select id="getStudentsByClazzIdStep2" resultType="student">
select sid, sname
from t_student
where cid = #{cid}
</select>
一对多的延迟加载和多对一的延迟加载开启方式一致。