十、Mybatis关联映射
以多个学生对应一个老师为例,存在:
- 学生关联老师,多对一关系
- 老师管理集合,一对多关系
1️⃣sql建表
create table `teacher`( `id` int not null, `name` varchar(30) default null, primary key(`id`) ) engine=InnoDB default charset=utf8; insert into teacher values (1,'王老师'); create table `student`( `id` int not null, `name` varchar(30) default null, `tid` int not null, primary key(`id`), key `FK_tid` (`tid`), constraint `FK_tid` foreign key(`tid`) references `teacher`(`id`) ) engine=InnoDB default charset=utf8;
2️⃣测试环境搭建
🍀(1)导入Lombok
🍀(2)新建Teacher,Student实体类
🍀(3)新建Mapper接口
🍀(4)在resources新建com->xxx->dao文件夹
🍀(5)新建xxxMapper.xml文件
🍀(6)在mybatis-config.xml中注册绑定xxxMapper.xml
🍀(7)在TeacherMapper接口中创建selectAll()方法
🍀(8)在TeacherMapper.xml中写对应的查询
🍀(9)新建测试类,在测试类中测试使用
3️⃣按照查询嵌套处理多对一
🍀实体类
Student.java:
@Data @AllArgsConstructor @NoArgsConstructor public class Student { private int id; private String name; private Teacher teacher; }
Teacher.java:
@Data @AllArgsConstructor @NoArgsConstructor public class Teacher { private int id; private String name; }
🍀Mapper接口
List<Student> selectAll();
🍀xxxMapper.xml
<!-- 查询思路: 1.查询所有学生 2.根据查询出的学生的tid查询老师,子查询 --> <resultMap id="student_teacher" type="Student"> <!-- property是实体类的属性 column是数据库的字段 --> <result property="id" column="id"/> <result property="name" column="name"/> <!-- 复杂的属性,需要单独处理,对象:association 集合collection --> <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/> </resultMap> <select id="selectAll" resultMap="student_teacher"> select * from mybatis.student </select> <select id="getTeacher" resultType="Teacher"> select * from mybatis.teacher where id=#{tid} </select>
🍀测试
@Test public void selectAll(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); List<Student> studentList = mapper.selectAll(); for (Student s: studentList) { System.out.println(s); } sqlSession.close(); }
4️⃣按照结果嵌套处理多对一
🍀Mapper接口
List<Student> selectAll2();
🍀xxxMapper.xml
<select id="selectAll2" resultMap="S_T"> select s.id sid,s.name sname,t.name tname from mybatis.student s,mybatis.teacher t where s.tid=t.id </select> <resultMap id="S_T" type="Student"> <result property="id" column="sid"/> <result property="name" column="sname"/> <association property="teacher" javaType="Teacher"> <result property="name" column="tname"/> </association> </resultMap>
🍀测试
@Test public void selectAll(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); List<Student> studentList = mapper.selectAll(); for (Student s: studentList) { System.out.println(s); } sqlSession.close(); }
5️⃣一对多关系的处理
🍀实体类
Student.java:
@Data @AllArgsConstructor @NoArgsConstructor public class Student { private int id; private String name; private int tid; }
Teacher.java:
@Data @AllArgsConstructor @NoArgsConstructor public class Teacher { private int id; private String name; //老师拥有多个学生 private List<Student> students; }
🍀Mapper接口
public interface TeacherMapper { List<Teacher> selectAll(); //获取指定老师下的所有学生 Teacher getTeacher(@Param("tid")int id); Teacher getTeacher2(@Param("tid")int id); List<Student> getStudents(@Param("tid")int id); }
🍀xxxMapper.xml
<select id="selectAll" resultType="Teacher"> select * from mybatis.teacher </select> <select id="getTeacher" resultMap="S_T"> select t.id tid, t.name tname,s.name sname from mybatis.teacher t,mybatis.student s where s.tid=tid and tid=#{tid} </select> <resultMap id="S_T" type="Teacher"> <result property="id" column="tid"/> <result property="name" column="tname"/> <!-- 集合中的泛型信息,我们使用ofType --> <collection property="students" ofType="Student"> <result property="name" column="sname"/> <result property="tid" column="tid"/> </collection> </resultMap> <select id="getTeacher2" resultMap="student_teacher"> select * from mybatis.teacher where id=#{tid} </select> <resultMap id="student_teacher" type="Teacher"> <result property="id" column="id"/> <result property="name" column="name"/> <collection property="students" column="id" ofType="Student" select="getStudents"/> </resultMap> <select id="getStudents" resultType="Student"> select * from mybatis.student where tid=#{tid} </select>
🍀测试
@Test public void selectAll(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); List<Student> studentList = mapper.selectAll(); for (Student s: studentList) { System.out.println(s); } sqlSession.close(); }
十一、Mybatis动态SQL
1️⃣动态SQL概述
MyBatis提供了对SQL语句动态的组装能力,大量的判断都可以在 MyBatis的映射XML文件里面配置,以达到许多我们需要大量代码才能实现的功能,大大减少了我们编写代码的工作量。
动态SQL的元素:
元素 | 作用 | 备注 |
if | 判断语句 | 单条件分支判断 |
choose、when、otherwise | 相当于Java中的 case when语句 | 多条件分支判断 |
trim、where、set | 辅助元素 | 用于处理一些SQL拼装问题 |
foreach | 循环语句 | 在in语句等列举条件常用 |
2️⃣if元素
if元素相当于Java中的if语句,它常常与test属性联合使用。现在我们要根据name去查找学生,但是name是可选的,如下所示:
<select id="findUserById" resultType="com.wang.entity.User"> select id,username,password from user where 1 =1 <if test="id != null"> AND id = #{id} </if> <if test="username != null and username != ''"> AND username = #{username} </if> <if test="password != null and password != ''"> AND password = #{password} </if> </select>
3️⃣where元素
上面的select语句我们加了一个1=1的绝对true的语句,目的是为了防止语句错误,变成SELECT * FROM student WHERE这样where后没有内容的错误语句。这样会有点奇怪,此时可以使用 < where> 元素。
<select id="findUserById" resultType="com.wang.entity.User"> select id,username,password from user <where> <if test="id != null"> AND id = #{id} </if> <if test="username != null and username != ''"> AND username = #{username} </if> <if test="password != null and password != ''"> AND password = #{password} </if> </where> </select>
4️⃣trim元素
有时候我们要去掉一些特殊的SQL语法,比如常见的and、or,此时可以使用trim元素。trim元素意味着我们需要去掉一些特殊的字符串,prefix代表的是语句的前缀,而prefixOverrides代表的是你需要去掉的那种字符串,suffix表示语句的后缀,suffixOverrides代表去掉的后缀字符串。
<select id="select" resultType="com.wang.entity.User"> SELECT * FROM user <trim prefix="WHERE" prefixOverrides="AND"> <if test="username != null and username != ''"> AND username LIKE concat('%', #{username}, '%') </if> <if test="id != null"> AND id = #{id} </if> </trim> </select>
5️⃣choose、when、otherwise元素
有些时候我们还需要多种条件的选择,在Java中我们可以使用switch、case、default语句,而在映射器的动态语句中可以使用choose、when、otherwise元素。
<!-- 有name的时候使用name搜索,没有的时候使用id搜索 --> <select id="select" resultType="com.wang.entity.User"> SELECT * FROM user WHERE 1=1 <choose> <when test="name != null and name != ''"> AND username LIKE concat('%', #{username}, '%') </when> <when test="id != null"> AND id = #{id} </when> </choose> </select>
6️⃣set元素
在update语句中,如果我们只想更新某几个字段的值,这个时候可以使用set元素配合if元素来完成。注意: set元素遇到,会自动把,去掉。
<update id="update"> UPDATE user <set> <if test="username != null and username != ''"> username = #{username}, </if> <if test="password != null and password != ''"> password = #{password} </if> </set> WHERE id = #{id} </update>
7️⃣foreach元素
foreach元素是一个循环语句,它的作用是遍历集合,可以支持数组、List、Set接口。
<select id="select" resultType="com.wang.entity.User"> SELECT * FROM user WHERE id IN <foreach collection="ids" open="(" close=")" separator="," item="id"> #{id} </foreach> </select>
collection配置的是传递进来的参数名称。
item配置的是循环中当前的元素。
index配置的是当前元素在集合的位置下标。
open和 close配置的是以什么符号将这些集合元素包装起来。
separator是各个元素的间隔符。
8️⃣foreach批量插入
<insert id="batchInsert" parameterType="list"> insert into `user`( user_name, pass) values <foreach collection="users" item="user" separator=","> (#{user.username}, #{user.password}) </foreach> </insert>
9️⃣SQL片段
有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。
提取SQL片段:
<sql id="if-title-author"> <if test="title != null"> title = #{title} </if> <if test="author != null"> and author = #{author} </if> </sql>
引用SQL片段:
<select id="queryBlogIf" parameterType="map" resultType="blog"> select * from blog <where> <!-- 引用 sql 片段,如果refid 指定的不在本文件中,那么需要在前面加上 namespace --> <include refid="if-title-author"></include> <!-- 在这里还可以引用其他的 sql 片段 --> </where> </select>