MyBatis动态SQL和高级映射,如何实现查询?

摘要:动态SQL if标签 一般应用在多条件查询中 <select id="selectByMultipleCondition" resultType=&q
动态SQL if标签 一般应用在多条件查询中 <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(只能处理前面的,不能处理后面的) <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标签 <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关键字,同时去掉最后多余的“,” 比如我们只更新提交不为空的字段,如果提交的数据是空或者“”,那么不更新这个字段。 <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 ... 只有一个分支会被执行。 <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标签 循环数组或集合时使用 批量删除 <!--对应的接口方法: 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> 批量插入 <!--对应的接口方法: 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语句当中。 主要为了代码复用。 <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类设计如下,添加一个班级类的属性,表示学生关联的班级对象。 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 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设计如下: <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设计如下: <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设计如下: <!--分步查询第二步:根据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文件中设置如下: <settings> <!--延迟加载的全局开关,默认关闭。 所有的分步查询,都采用延迟加载。 实际开发中建议开启。--> <setting name="lazyLoadingEnabled" value="true"/> </settings> 在开启全局延迟加载的情况下,如果某个分步查询不需要开启延迟加载怎么办? 在对应的association标签中添加fetchType="eager",就可以关闭这个查询的延迟加载。 一对多 一个班级对应多个学生 一对多的映射实现方式有两种: collection 分步查询 第一种方式:collection 班级类设计如下: 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文件设计如下: <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文件设计如下: <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设计如下: <select id="getStudentsByClazzIdStep2" resultType="student"> select sid, sname from t_student where cid = #{cid} </select> 一对多的延迟加载和多对一的延迟加载开启方式一致。