一、动态sql的简述
什么是动态sql:在不同条件下拼接不同的sql
Mybatis框架的动态sql技术是一种根据特定条件动态拼接SQl语句的功能,他存在的意义是为了解决拼接SQL语句字符串时的痛点问题。比如我们在用淘宝之类的软件在进行商品属性选择的时候,我们会发现我们可以选择的商品的属性有很多条件,其中一些条件可以选择也可以不选择,那么如果使用传统的方式进行查询,反而在拼接sql的时候会造成一些列的问题。
二、动态sql的使用案例
1. if标签—(讲课注意:username和sex必须一个为空)
根据 username 和 sex 来查询数据。如果username为空,那么将只根据sex来查询;反之只根据username来查询
首先先不使用动态sql
<select id="selectUserByUsernameAndSex" parameterType="com.qcby.entity.User"
resultType="com.qcby.entity.User">
select * from user where username = #{username} and sex = #{sex}
</select>
上面的查询语句,我们可以发现,如果 #{username} 为空,那么查询结果也是空,如何解决这个问题呢?使用 if 来判断
使用动态sql
<select id="selectUserByUsernameAndSex" parameterType="com.qcby.entity.User"
resultType="com.qcby.entity.User">
select * from user where
<if test="username != null and username = ''">
username=#{username}
</if>
<if test="sex != null and sex !=''">
and sex=#{sex}
</if>
</select>
从上边的案例当中我们可以看出如果 sex 等于 null,那么查询语句为 select * from user where username=#{username},如果username等于null,那么查询语句为select * from user where and sex=#{sex} 那这显然是不对的。
2. where — if 标签
<select id="selectUserByUsernameAndSex" parameterType="com.qcby.entity.User"
resultType="com.qcby.entity.User">
select * from user
<where>
<if test="username != null">
username=#{username}
</if>
<if test="sex != null">
and sex=#{sex}
</if>
</where>
</select>
但是我们加上了 标签,如果username为空怎么办?sex当中的and会不会受影响?
从上图的案例当中我们可以看到如果标签返回的内容是以AND 或OR 开头的,则它会剔除掉。
3、set–if 标签 用来组装update语句
dao层
/**
* 修改
* @param user
* @return
*/
public int update(User user);
mapping
<update id="update" parameterType="com.qcby.entity.User">
update user
<set>
<if test="username !=null and username!=''">
username = #{username} ,
</if>
<if test="address != null and address != ''">
address = #{address}
</if>
</set>
where id = #{id}
</update>
3.测试
@Test
public void update(){
User user = new User();
user.setId(6);
user.setSex("女");
int code = mapper.updateByDo(user);
session.commit();
System.out.println(code);
}
4、choose----when和otherwise标签
这个标签相当于是我们java当中的if…elseif…else
choose 标签是这个标签组合当中的父标签和标签都在标签内部。
when 标签就相当于是我们的 if 和 elseif
othrtwise 标签相当于是我们的 else
<select id="selectUserByChoose" resultType="com.qcby.entity.User"
parameterType="com.qcby.entity.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>
5、trim 标签
trim标记是一个格式化的标记,可以完成set或者是where标记的功能
①、用 trim 改写上面第二点的 if+where 语句
<select id="selectUserByUsernameAndSex" parameterType="com.qcby.entity.User"
resultType="com.qcby.entity.User">
select * from user
<!-- <where>-->
<!-- <if test="username != null">-->
<!-- username=#{username}-->
<!-- </if>-->
<!-- <if test="sex != null">-->
<!-- and sex=#{sex}-->
<!-- </if>-->
<!-- </where>-->
<trim prefix="where" prefixOverrides="and | or">
<if test="username != null">
and username=#{username}
</if>
<if test="sex != null">
and sex=#{sex}
</if>
</trim>
</select>
prefix:前缀
prefixoverride:去掉第一个and或者是or
②、用 trim 改写上面第三点的 if+set 语句
<update id="update" parameterType="com.qcby.entity.User">
update user
<!-- <set>-->
<!-- <if test="username !=null and username!=''">-->
<!-- username = #{username} ,-->
<!-- </if>-->
<!-- <if test="address != null and address != ''">-->
<!-- address = #{address}-->
<!-- </if>-->
<!-- </set>-->
<trim prefix="set" suffixOverrides=",">
<if test="username != null and username != ''">
username = #{username},
</if>
<if test="sex != null and sex != ''">
sex = #{sex},
</if>
</trim>
where id = #{id}
</update>
6、标签
有些时候我们的数据是以数组的形式出现的,比如我们进行批量删除和批量添加的时候
①:批量删除
<!-- 批量删除的sql语句:delete from user where id in (1,2,3,4,5); -->
<delete id="deleteMoreByArray">
delete from user where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
<!-- collection:当前要循环的数组或者集合 -->
<!-- item: 我们指定要循环的数组的每一个元素 -->
<!-- separator:每一个元素应该用什么来做分割 -->
<!-- open:当前循环是以什么开始 -->
<!-- close:当前循环是以什么结束 -->
/**
* 通过数组批量删除
* @param ids
* @return
*/
int deleteMoreByArray(@Param("ids") Integer[] ids);
@Test
public void deleteMoreByArray(){
int result = mapper.deleteMoreByArray(new Integer[]{1,2,3});
session.commit();
System.out.println(result);
}
第二种写法
②:批量添加
<insert id="insertMoreByList" >
insert into user(id,username,birthday,sex,address) values
<foreach collection="users" item="user" separator=",">
(null,#{user.username},#{user.birthday},#{user.sex},#{user.address})
</foreach>
</insert>
/**
* 通过集合批量添加
* @param users
* @return
*/
int insertMoreByList(@Param("users") List<User> users);
@Test
public void insertMoreByList(){
User user1 = new User("a1","男","北京");
User user2 = new User("a2","男","北京");
User user3 = new User("a3","男","北京");
List<User> users = Arrays.asList(user1,user2,user3);
int result = mapper.insertMoreByList(users);
session.commit();
System.out.println(result);
}
三、关联映射
一、创建表结构
学生表
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`Sname` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`sex` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`t_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES (1, '张三', '男', 18, 1);
INSERT INTO `student` VALUES (2, '李四', '女', 18, 1);
INSERT INTO `student` VALUES (3, '王五', '男', 18, 1);
INSERT INTO `student` VALUES (4, '小白', '女', 18, 1);
INSERT INTO `student` VALUES (5, '小黑', '男', 18, 1);
INSERT INTO `student` VALUES (6, '小红', '女', 20, 2);
INSERT INTO `student` VALUES (7, '小李', '男', 20, 2);
INSERT INTO `student` VALUES (8, '小张', '女', 20, 2);
INSERT INTO `student` VALUES (9, '小赵', '男', 20, 2);
INSERT INTO `student` VALUES (10, '小王', '女', 20, 2);
SET FOREIGN_KEY_CHECKS = 1;
老师表
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for teacher
-- ----------------------------
DROP TABLE IF EXISTS `teacher`;
CREATE TABLE `teacher` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`Tname` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Records of teacher
-- ----------------------------
INSERT INTO `teacher` VALUES (1, '张老师');
INSERT INTO `teacher` VALUES (2, '李老师');
SET FOREIGN_KEY_CHECKS = 1;
二、一对一、多对一映射
1.第一种形式 连表查询
SELECT student.id,student.name,teacher.name FROM student LEFT JOIN teacher on student.t_id = teacher.id
①:设置实体类
public class Student {
private Integer id;
private String Sname;
private String sex;
private Integer age;
private Integer t_id;
//这个是重点
private Teacher teacher;
}
public class Teacher {
private Integer id;
private String Tname;
}
②:查询语句
<!-- 按照结果嵌套处理-->
<select id="getStudent1" resultMap="StudentTeacher1">
SELECT student.id,student.Sname,teacher.Tname FROM student LEFT JOIN teacher on student.t_id = teacher.id
</select>
<resultMap id="StudentTeacher1" type="com.qcby.entity.Student">
<result property="id" column="id"/>
<result property="Sname" column="Sname"/>
<result property="sex" column="sex"/>
<result property="age" column="age"/>
<result property="t_id" column="t_id"/>
<!-- 复杂的属性我们需要单独去处理 对象:association 集合:collection -->
<!-- property="teacher" student类当中的关联字段 -->
<!-- javaType="com.javen.model.Teacher" 为复杂属性设置类类型-->
<association property="teacher" javaType="com.qcby.entity.Teacher">
<result property="id" column="id"/>
<result property="Tname" column="Tname"/>
</association>
</resultMap>
查询每个学生的对应的老师
2.第二种形式 分步查询
SELECT s.id,s.Sname,t.Tname FROM student s,teacher t where s.t_id = t.id
①:设置实体类
这里的实体类不需要变动
②:查询语句
<select id = "getStudent" resultMap="StudentTeacher">
select * from student;
</select>
<!--结果映射集-->
<resultMap id="StudentTeacher" type="com.qcby.entity.Student">
<result property="id" column="id"/>
<result property="Sname" column="Sname"/>
<result property="sex" column="sex"/>
<result property="age" column="age"/>
<result property="t_id" column="t_id"/>
<!-- select="getTeacher" :调用下一个查询语句 -->
<!-- column="t_id" 两个表的关联字段-->
<association property="teacher" column="t_id" javaType="com.qcby.entity.Teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="com.qcby.entity.Teacher">
select * from teacher where id = #{t_id}; <!-- #{id}; 可以写任何东西,因为会自动匹配 t_id -->
</select>
三、一对多关联映射
查询每个老师有多少学生
1.第一种形式:按照结果嵌套处理
SELECT teacher.id,teacher.name,student.name FROM teacher
LEFT JOIN student on student.t_id = teacher.id
①.设置实体类
public class Student {
private Integer id;
private String Sname;
private String sex;
private Integer age;
private Integer t_id;
}
这里需要将student类当中关于teacher的字段删除
public class Teacher {
private Integer id;
private String Tname;
//这个一定要有
private List<Student> students;
}
②.查询语句
<!--按照结果进行查询-->
<select id="getTeacher" resultMap="TeacherStudent">
SELECT teacher.id,teacher.Tname,student.Sname FROM teacher
LEFT JOIN student on student.t_id = teacher.id
</select>
<resultMap id="TeacherStudent" type="com.qcby.entity.Teacher">
<result property="id" column="id"/>
<result property="Tname" column="Tname"/>
<!-- 复杂的属性我么需要单独去处理 对象:association 集合:collection
在集合中的泛型信息,我们使用ofType获取
-->
<collection property="students" ofType="com.qcby.entity.Student">
<result property="Sname" column="Sname"/>
</collection>
</resultMap>
2.第二种形式:按照查询嵌套处理
SELECT s.id,s.Sname,t.Tname FROM student s,teacher t where s.t_id = t.id
①.设置实体类
这里的实体类不需要变动
②.查询语句
<!--按照查询嵌套处理-->
<select id="getTeacher" resultMap="TeacherStudent2">
select * from teacher
</select>
<resultMap id="TeacherStudent2" type="com.qcby.entity.Teacher">
<collection property="students" column="id"
ofType="com.qcby.entity.Student" select="getStudentByTeacherId" />
</resultMap>
<select id="getStudentByTeacherId" resultType="com.qcby.entity.Student">
select * from student where t_id = #{t_id}
</select>
MyBatis延迟加载策略
以上有两种写法来表示查询信息,分别是链表查询和分步查询的方法。那么既然我么能用一个SQL语句能够执行完,那为什么还要分开来写呢?
原因很简单:同学们可以发现如果我们把他们连在一起那么他们就是一个多表查询语句,如果不放在一起执行,那那就是单独一个表的查询语句。但是这需要我们设置mybatis的延迟加载(懒加载)
分步查询的优点:可以实现延迟加载,但是必须在核心配置文件中设置全局配置信息
lazyLoadingEnabled: 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载
aggressiveLazyLoding: 当开启时,任何方式的调用都会加载该对象的所有属性。否则,该属性会按需加载
此时就可以实现按需加载,需要获取的数据是什么,就只会执行相应的sql.此时会通过association和collection中的fetchType属性设置当前的分步查询是否使用懒加载
fetchType=“lazy(延迟加载) | eager(立即加载)”
1.在主配置文件当中设置延迟加载
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
2.在studentDao.xml当中设置分步查询
<select id = "getStudent" resultMap="StudentTeacher">
select * from student;
</select>
<!--结果映射集-->
<resultMap id="StudentTeacher" type="com.qcby.entity.Student">
<result property="id" column="id"/>
<result property="Sname" column="Sname"/>
<result property="sex" column="sex"/>
<result property="age" column="age"/>
<result property="t_id" column="t_id"/>
<!-- select="getTeacher" :调用下一个查询语句 -->
<!-- select="com.qcby.dao.TeacherDao.getTeacher" :调用下一个查询语句 -->
<association property="teacher" column="t_id" javaType="com.qcby.entity.Teacher"
select="com.qcby.dao.TeacherDao.getTeacher" fetchType="lazy"/>
</resultMap>
3.在TeacherDao.xml当中设置关于教师的查询
<select id="getTeacher" resultType="com.qcby.entity.Teacher" parameterType="java.lang.Integer">
select * from teacher where id = #{t_id}; <!-- #{id}; 可以写任何东西,因为会自动匹配 t_id -->
</select>
4.配置TeacherDao
Teacher getTeacher(Integer id);
5.访问设置
①:只访问student当中的内容
@Test
public void getStudent(){
List<Student> student = mapper.getStudent();
for (Student student1:student) {
System.out.println(student1.getSex());
}
}
②:访问全部的内容和有关Teacher表当中的内容时
@Test
public void getStudent(){
List<Student> student = mapper.getStudent();
for (Student student1:student) {
// System.out.println(student1);
System.out.println(student1.getTeacher().getTname());
}
}
6.一些特殊情况下的查询
我们在主配置文件当中设置的懒加载,对于任何一个分步查询都是有效的,但是在一些特殊的情况下,我们希望有些语句不分开查询,这个时候我们就需要设置该语句只能立即加载
<select id = "getStudent" resultMap="StudentTeacher">
select * from student;
</select>
<!--结果映射集-->
<resultMap id="StudentTeacher" type="com.qcby.entity.Student">
<result property="id" column="id"/>
<result property="Sname" column="Sname"/>
<result property="sex" column="sex"/>
<result property="age" column="age"/>
<result property="t_id" column="t_id"/>
<!-- select="com.qcby.dao.TeacherDao.getTeacher" :调用下一个查询语句 -->
<!-- column="t_id" 两个表的关联字段-->
<!--fetchType="eager" 立即加载-->
<association property="teacher" column="t_id" javaType="com.qcby.entity.Teacher"
select="com.qcby.dao.TeacherDao.getTeacher" fetchType="eager"/>
</resultMap>
测试语句
@Test
public void getStudent(){
List<Student> student = mapper.getStudent();
for (Student student1:student) {
System.out.println(student1.getSex());
}
}