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. 查询数据库
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,755评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,369评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,799评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,910评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,096评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,159评论 3 411
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,917评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,360评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,673评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,814评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,509评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,156评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,123评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,641评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,728评论 2 351

推荐阅读更多精彩内容

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