Mybatis笔记二

Mybatis

八、复杂查询处理

1、复杂环境准备

  • 创建表tb_teacher和tb_student
  • pom.xml中依赖包【数据库驱动包、Mybatis依赖包、测试包Junit、Lombok】
  • 实现SqlSessionUtil工具类
  • 编写实体类dto【Teacher和Student】
  • 编写数据持久层dao【mapper接口和mapper.xml配置文件】
  • 测试

2、一对多实现

问题描述:查询某个老师以及他所教的学生信息

Student

@Data
public class Student {
    private int sid;
    private String sname;
    private int tid;
}

Teacher

@Data
public class Teacher {
    private int tid;
    private String tname;
    private List<Student> students;
}

TeacherMapper

public interface TeacherMapper {

    //问题:查询某个老师以及他所教的学生信息
    Teacher queryByTid(int tid);

    Teacher queryByTid2(int tid);

}

TeacherMapper.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.TeacherMapper">
    <!-- 方式一:使用联合查询 -->
    <resultMap id="ts" type="teacher">
        <!-- 将结果中的字段与实体类中的属性对应 -->
        <result property="tid" column="tid" ></result>
        <result property="tname" column="tname" ></result>
        <!-- 试题类中有集合,使用collection映射
            property:属性名
            ofType:对应着集合中泛型所限定的类【List<Student>】
         -->
        <collection property="students" ofType="student">
            <result property="tid" column="tid" ></result>
            <result property="sid" column="sid"></result>
            <result property="sname" column="sname"></result>
        </collection>
    </resultMap>
    <select id="queryByTid" resultMap="ts">
        select sid, sname, t.tid as tid, tname
        from tb_teacher t, tb_student s
        where t.tid = s.tid
        and t.tid = #{tid};
    </select>

    <!-- 方式二:子查询的方式进行查询 -->
    <select id="queryByTid2" resultMap="ts2">
        select tid, tname from tb_teacher where tid = #{tid};
    </select>

    <resultMap id="ts2" type="teacher">
        <!-- 将查询的结果的字段名和实体类中的属性名做映射 -->
        <result property="tid" column="tid" ></result>
        <!-- collection实体类中存在集合
            property:实体类中的集合属性名
            javaType:集合属性所对应的类型【可以省略不写】
            ofType:集合中泛型所限定的类型【List<Student>】
            select:对应的子查询的唯一标识id
            column:子查询中所需要用到的参数
         -->
        <collection property="students" javaType="ArrayList" ofType="student" select="queryStudent" column="tid"></collection>
    </resultMap>

    <select id="queryStudent" resultType="student">
        select sid, sname from tb_student where tid = #{tid};
    </select>

</mapper>

3、多对一实现

问题描述:查询某个老师以及他所教的学生信息

Student

@Data
public class Student {
    private int sid;
    private String sname;
    private Teacher teacher;
}

Teacher

@Data
public class Teacher {
    private int tid;
    private String tname;
}

StudentMapper

public interface StudentMapper {

    //查询所有的学生以及对应的授课老师
    List<Student> getAll();

    List<Student> getAll2();
}

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.ch.dao.StudentMapper">

    <!-- 方式一: 使用联合查询的方式查询 -->
    <resultMap id="st" type="student">
        <!-- 将查询的结果字段与实体类的属性一一映射 -->
        <result property="sid" column="sid"></result>
        <result property="sname" column="sname"></result>
        <!-- 实体类中包含teacher对象,使用association将它与Teacher实体类中的属性映射
            property:对应的属性名
            javaType:属性名对应的类型
         -->
        <association property="teacher" javaType="teacher">
            <result property="tid" column="tid"></result>
            <result property="tname" column="tname"></result>
        </association>
    </resultMap>
    <select id="getAll" resultMap="st">
        select sid, sname, t.tid tid, tname
        from tb_teacher t, tb_student s
        where t.tid = s.tid;
    </select>


    <!-- 方式二: 使用子查询的方式查询 -->
    <select id="getAll2" resultMap="st2">
        select sid, sname, tid from tb_student;
    </select>

    <resultMap id="st2" type="student">
        <!-- 将查询的结果字段与实体类的属性一一映射 -->
        <result property="sid" column="sid"></result>
        <result property="sname" column="sname"></result>
        <!-- 实体类中包含teacher对象,使用association将它与Teacher实体类中的属性映射
            property:对应的属性名
            javaType:属性名对应的类型【可以省略】
            column:需要传入子查询的参数
            select:子查询对应的唯一标识id
         -->
        <association property="teacher" javaType="teacher" column="tid" select="queryTeacher"></association>
    </resultMap>

    <select id="queryTeacher" resultType="teacher">
        select tid,tname from tb_teacher where tid = #{tid};
    </select>

</mapper>

九、动态SQL

理解什么是动态SQL?

  • 动态SQL就是根据不同的条件生成不同的SQL语句。
  • 动态SQL本质还是SQL,只是在SQL层面,添加一些逻辑代码
if
choose (when, otherwise)
trim (where, set)
foreach

if

//1、if
//如果传入sid,则根据sid查询,如果传入sname,则根据sname查询,如果都不穿,则查询所有
List<Student> queryByIf(Map<String, Object> map);
<!-- if测试 -->
<select id="queryByIf" resultType="com.ch.dto.Student" parameterType="map">
    select * from tb_student where 1 = 1
    <if test="sid != null">
        and sid = #{sid};
    </if>
    <if test="sname != null ">
        and sname = #{sname};
    </if>
</select>

choose(when,otherwise)

//2、choose
//如果传入sname,则根据sname查询,否则就根据sid查询
List<Student> queryByChoose(Map<String, Object> map);
<!-- choose测试 -->
<select id="queryByChoose" resultType="com.ch.dto.Student" parameterType="map">
    select * from tb_student where 1 = 1
    <choose>
        <when test="sid != null">
            and sname = #{sname};
        </when>
        <otherwise>
            and sid = #{sid};
        </otherwise>
    </choose>
</select>

trim(where,set)

显然写where 1 = 1是不安全的,所以使用where标签

where

  • 动态添加where
  • 自动判断是否需要添加或去掉and|or
//如果传入sid,则根据sid查询,如果传入sname,则根据sname查询,如果都传则两条件一起查询,如果都不传,则查询所有
List<Student> queryByWhere(Map<String, Object> map);
<!-- where测试 -->
<select id="queryByWhere" resultType="com.ch.dto.Student" parameterType="map">
    select * from tb_student
    <where>
        <if test="sid != null">
            sid = #{sid}
        </if>
        <if test="sname != null">
            and sname = #{sname}
        </if>
    </where>
</select>

set

  • 动态添加set
  • 自动判断是否需要去掉多余的逗号“,”
//如果传入sname,则修改sname,如果传入tid,则修改tid,如果都传则两字段一起修改
int updateBySet(Map<String, Object> map);
<!-- set测试 -->
<update id="updateBySet" parameterType="map">
    update tb_student
    <set>
        <if test="sid != null">
            tid = #{tid}
        </if>
        <if test="sname != null">
            , sname = #{sname}
        </if>
    </set>
    where sid = #{sid};
</update>

foreach

//查询id = 1 or id = 2 or id = 3【不使用in】
List<Student> queryByForeach(List<Integer> list);
<!-- foreach测试 -->
<select id="queryByForeach" resultType="com.ch.dto.Student" parameterType="list">
    select * from tb_student
    <where>
        <!-- foreach
                collection:遍历我们所传递的集合
                item:从我们集合中遍历出来的每一项
                open:以...开始
                close:以...结束
                separator:分隔符是什么
            -->
        <foreach collection="list" item="sid" open="(" close=")" separator="or">
            sid = #{sid}
        </foreach>
    </where>
</select>

SQL片段

抽取一个公共的部分

  • 最好是单表,简单的SQL语句
  • 里面最好不使用where
<sql id="pub">
    <if test="sid != null">
        sid = #{sid}
    </if>
    <if test="sname != null">
        and sname = #{sname}
    </if>
</sql>

<!-- where测试 -->
<select id="queryByWhere" resultType="com.ch.dto.Student" parameterType="map">
    select * from tb_student
    <where>
        <include refid="pub"></include>
        <!--            <if test="sid != null">-->
        <!--                sid = #{sid}-->
        <!--            </if>-->
        <!--            <if test="sname != null">-->
        <!--                and sname = #{sname}-->
        <!--            </if>-->
    </where>
</select>

十、缓存

缓存有什么用?

在没有增删改的情况下,不需要去查询数据库,减轻数据库的压力

一级缓存

一级缓存默认是开启的

  • 一级缓存是基于SqlSession级别的缓存,缓存在创建SqlSession到提交或者关闭期间有效

二级缓存

二级缓存需要手动开启

  • 二级缓存是namespace级别的缓存,只要查询的是当前namespace下的数据,那么缓存均有效

如何开启?

1、在mybatis-config.xml中开启全局缓存(二级缓存)

<!-- 开启全局缓存 -->
<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>

2、在需要使用二级缓存的mapper.xml中配置

<cache/>

或者自定义参数配置

<cache
  eviction="FIFO"
  flushInterval="60000"
  size="512"
  readOnly="true"/>

这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

可用的清除策略有:

  • LRU – 最近最少使用:移除最长时间不被使用的对象。
  • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
  • SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
  • WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。

默认的清除策略是 LRU。

flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。

size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。

readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。

特别注意:实体类都应该实现序列化接口,不然二级缓存使用的时候会报错........

缓存原理

  1. 数据缓存先缓存到一级缓存
  2. SqlSession提交或者关闭后,则将一级缓存的数据放到二级缓存中
posted @ 2020-08-10 17:19  itch  阅读(189)  评论(0编辑  收藏  举报