MyBatis 入门笔记+理解(二)

这是基于b站狂神的JavaWeb课程的笔记 课程链接
笔记第一部分链接:MyBatis 入门笔记+理解(一)

9. 复杂查询之多对一

9.1 环境搭建

  • sql建表
CREATE TABLE `teacher` (
  `id` INT(10) NOT NULL,
  `name` VARCHAR(30) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO teacher(`id`, `name`) VALUES (1, 秦老师); 

CREATE TABLE `student` (
  `id` INT(10) NOT NULL,
  `name` VARCHAR(30) DEFAULT NULL,
  `tid` INT(10) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `fktid` (`tid`),
  CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (1, 小明, 1); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (2, 小红, 1); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (3, 小张, 1); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (4, 小李, 1); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (5, 小王, 1);
  • 构造实体类

学生

@Data
public class Student {
    private int id;
    private String name;
    // 学生需要关联一个老师
    private Teacher teacher;
}

老师

@Data
public class Teacher {
    private int id;
    private String name;
}

9.2 多对一

  • 现有需求:获取所有学生的信息和他们关联的老师的信息
    多名学生和一个老师关联
    需求对应的sql语句:
select s.id,s.name,t.id,t.name 
from student s, teacher t 
where s.tid = t.id;
  • 思路:
    1. 查询出所有学生的信息
    2. 根据查出来学生的tid,查询出对应关联的老师的信息
  • 根据思路编写Mapper.xml:
    方法一:根据查询嵌套处理
    <!--  根据查询嵌套处理  -->
    <!--  1. 查询所有学生信息  -->
    <select id="getStudents" resultMap="studentAndTeacher">
        select * from student
    </select>

    <!--  两次查询用一个resultMap关联起来  -->
    <resultMap id="studentAndTeacher" type="student">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <!--
            复杂的属性,单独处理:对象用association,集合用collection
            属性是一个对象,所以先用javaType来规定是哪个类
            而从表中获得的是一个tid,所以需要用一个字查询语句,用tid获取整个Teacher对象
            select="getTeachers"就规定了用如下的这个sql去获得teacher的信息
        -->
        <association property="teacher" column="tid" javaType="Teacher" select="getTeachers"/>
    </resultMap>

    <!--  2. 根据学生查出来的tid,查询对应的老师! (子查询)  -->
    <select id="getTeachers" resultType="teacher">
        select * from teacher where id=#{tid}
    </select>
  • 本质上,第一个 <select>select * from student 得到的表字段为 tid 而学生类的属性为 teacher,所以需要一个结果集映射将他们对应起来
  • 而恰好 teacher 是一个对象 (用 javaType="Teacher" 表示),不能简单地只将名称进行映射,还需要得到 teacher 的内部属性信息,所以我们又需要一个子查询,就是第二个 <select> 中的sql查询。

方法二:根据结果嵌套处理

    <!--  根据结果嵌套处理  -->
    <select id="getStudents2" resultMap="mapTeacher">
        select s.id sid, s.name sname, t.id tid, t.name tname
        from student s, teacher t
        where s.tid = t.id
    </select>

    <resultMap id="mapTeacher" type="Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <association property="teacher" javaType="Teacher">
            <!--     
                根据查询到的结果(tid, tname),
                来与Teacher这个类中的属性进行映射
            -->
            <result property="id" column="tid"/>
            <result property="name" column="tname"/>
        </association>
    </resultMap>

10. 复杂查询之一对多

与9中的情况相对应,一个老师拥有多个学生,此时称为一对多

11.1 环境搭建

  • 表与9中相同
  • 实体类
    老师
@Data
public class Teacher {
    private int id;
    private String name;
    // 一个老师拥有多个学生
    private List<Student> students;
}

学生

@Data
public class Student {
    private int id;
    private String name;
    private int tid;
}

10.2 一对多处理

  • 需求:根据id查询老师信息和这个老师拥有的所有学生信息
  • sql:
select t.id tid, t.name tname, s.id sid, s.name sname
from student s, teacher t 
where t.id = s.tid and t.id = #{tid}
  • 接口:
public interface TeacherMapper {

    // 根据id查询老师信息和这个老师拥有的所有学生信息
    // 使用@param("tid")后,则Mapper.xml中的sql语句中应用对应的#{tid}
    // 提取该参数
    Teacher getTeacher(@Param("tid") int id);
}

方法一:根据结果嵌套处理

    <!--  根据结果嵌套处理  -->
    <select id="getTeacher" resultMap="tsMap">
        select t.id tid, t.name tname, s.id sid, s.name sname
        from student s, teacher t 
        where t.id = s.tid and t.id = #{tid}
    </select>
    
    <resultMap id="tsMap" type="teacher">
        <!--    名称映射    -->
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
        <!--
            属性为集合时 ==> collection
            集合中的泛型用 ofType 规定
        -->
        <collection property="students" ofType="student">
            <result property="id" column="sid"/>
            <result property="name" column="sname"/>
        </collection>
    </resultMap>
  • 先拿到结果 t.id tid, t.name tname, s.id sid, s.name sname
  • 再用拿到的这些结果与老师的属性,以及老师拥有的学生的属性作一一映射

方法二:根据查询嵌套处理

    <!--  根据查询嵌套处理  -->
    <!--  先根据id拿到老师  -->
    <select id="getTeacher2" resultMap="tsMap2">
        select * from teacher
        where id = #{tid}
    </select>

    <resultMap id="tsMap2" type="teacher">
        <!--  
            注意:这里即使名称相同,也要显式地映射一次
            因为column中的id在下面被映射给了"students"
            这里不映射会导致查不到teacher的id
        -->
        <result property="id" column="id"/>
        <!--
            属性名称为students
            类为Arraylist,集合中的泛型为student
            再用一个子查询去查tid为"id"的学生
        -->
        <collection property="students" javaType="Arraylist" ofType="student" column="id" select="getStudent"/>
    </resultMap>

    <!--  再根据老师的id拿到tid为老师的id的学生  -->
    <select id="getStudent" resultType="student">
        select * from student
        where tid = #{id}
    </select>

11. 动态SQL

  • 减少了根据不同条件拼接 SQL 语句的痛苦

11.1 If

一个官方文档中的例子足矣:

<select id="findActiveBlogWithTitleLike"
     resultType="Blog">
  SELECT * FROM BLOG
  WHERE state = ‘ACTIVE’
  <if test="title != null">
    AND title like #{title}
  </if>
</select>

一般将一个 Map 作为参数,若在 Map 中放入了 title 的值,则可以被取出并放到 <if> 中的 AND 语句里

11.2 Choose

与Java中的 switch 类似:

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <choose>
    <when test="title != null">
      AND title like #{title}
    </when>
    <when test="author != null and author.name != null">
      AND author_name like #{author.name}
    </when>
    <otherwise>
      AND featured = 1
    </otherwise>
  </choose>
</select>

根据 <when> 的编写顺序查看是否满足条件,只会执行第一条满足条件的 <when> ,若没有满足条件的则执行 <otherwise>

11.3 Where, Set, Trim

Where
<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG
  <where>
    <if test="state != null">
         state = #{state}
    </if>
    <if test="title != null">
        AND title like #{title}
    </if>
    <if test="author != null and author.name != null">
        AND author_name like #{author.name}
    </if>
  </where>
</select>

官方doc一句话:

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

也就是说在上面这个例子中,若没有一个 if 被满足,where将自动消失,若只有第二个 if 或 只有第三个 if 被满足,他们前面的 AND 将自动消失。

Set
<update id="updateAuthorIfNecessary">
  update Author
    <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
  where id=#{id}
</update>

这个例子中,set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号

trim

可自定义 where

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
</trim>

也可自定义 set

<trim prefix="SET" suffixOverrides=",">
  ...
</trim>

11.4 foreach

需求的SQL:

SELECT * FROM user 
WHERE gender = 'male'
AND ( id = 1 OR id = 2 OR id = 4 )
  • 在接口的参数中使用 Map<String, Object>() map
  • 在map中添加一个list
ArrayList<Integer> ids = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
map.put("ids", ids)

在mapper.xml中编写

<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM user
  WHERE Gender = #{male}
  <foreach item="id" collection="ids"
      open=" AND (" separator="OR" close=")">
        #{id}
  </foreach>
</select>

12. 缓存

12.1 一级缓存

  • 默认开启
  • Sqlsession 级别的缓存
  • 通俗地说,一级缓存只存在于其对应的一个 Sqlsession 的生命周期中:
// 对于这个sqlSession的缓存,只存在于这两行代码之间
SqlSession sqlSession = MyBatisUtils.getSqlSession();
...
sqlSession.close()
  • 在一个 sqlSession 中,查询同一个东西,只会在第一次查询那个东西时连接到数据库,之后都会直接从缓存中拿去
  • 有任何增、删、改的操作都会清除缓存
  • sqlSession.clearCache() 也可手动清除缓存
  • 一级缓存就是一个Map

12.2 二级缓存

  • 基于 namespace 级别的缓存
  • 需手动开启
    <!-- 显式地开启全局缓存>
    <setting name="cacheEnabled" value="true"/>
    
    <!-- 在当前Mapper.xml中使用二级缓存 -->
    <cache/>
    
    也可以自定义参数
    <cache
    eviction="FIFO"
    flushInterval="60000"
    size="512"
    readOnly="true"/>
    
  • 开启后,当一个会话结束了,其对应的缓存在消失之前会进入到二级缓存,也就是当前Mapper的缓存中

12.3 查看缓存的顺序

在一次会话中做一次查询

  1. 先查看当前Mapper中的二级缓存中有没有查询结果,若没有
  2. 再查看当前会话对应的一级缓存中有没有,若没有
  3. 查询数据库
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 1.JDBC有哪些不足,MyBatis是如何解决的① 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如...
    机智的柠檬阅读 522评论 0 0
  • 这是学习颜群老师的mybatis教程[https://www.bilibili.com/video/BV1gs41...
    wangailiu阅读 472评论 0 0
  • 1、什么是Mybatis? (1)Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,开发时只...
    青青子衿zq阅读 152评论 0 0
  • 1、MyBatis简介 MyBatis 是一款优秀的持久层框架 中文官网:https://mybatis.org/...
    CHeng_c0e9阅读 628评论 0 0
  • 1 Mybatis入门 1.1 单独使用jdbc编程问题总结 1.1.1 jdbc程序 上边使...
    哇哈哈E阅读 3,459评论 0 38

友情链接更多精彩内容