MyBatis学习笔记--下篇

MyBatis学习笔记–下篇

文章目录

  • MyBatis学习笔记--下篇
    • 1、多对一的处理(关联)
      • 1.1、表的创建
      • 1.2、实体类
      • 1.2、按照查询嵌套处理
      • 1.3、按照结果嵌套处理(联表查询 )
    • 2、一对多的处理(Collection)
      • 2.1、实体类
      • 2.2、按结果查询(联表查询)
      • 2.3、按照结果嵌套查询
    • 3、动态Sql
      • 3.1、表的创建
      • 3.2、动态sql中三大标签的使用
        • 3.2.1、if
        • 3.2.2、choose(when,otherwise)
        • 3.2.3、 trim(where,set)
        • 3.2.4、 for each
        • 3.2.5、 Sql片段
    • 4、缓存
      • 4.1、简介
      • 4.2、Mybatis缓存
      • 4.3、一级缓存(作用小)
      • 4.4、二级缓存
      • 4.5、缓存原理
      • 4.6、自定义缓存--Ehcache
    • 5、问题

1、多对一的处理(关联)

MyBatis学习笔记--下篇_第1张图片

  • 多个学生,对应一个老师
  • 对于学生而言,关联…多个关联一个老师
  • 对于老师而言,**集合…**一个老师有多个学生
    在这里插入图片描述

1.1、表的创建

Teacher表

DROP TABLE IF EXISTS `teacher`;
CREATE TABLE `teacher` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='老师表';

-- ----------------------------
-- Records of teacher
-- ----------------------------
INSERT INTO `teacher` VALUES ('1', 'radan');
INSERT INTO `teacher` VALUES ('2', 'js');

Student表

DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `tid` int DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `tid` (`tid`),
  CONSTRAINT `tid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES ('1', '张三', '1');
INSERT INTO `student` VALUES ('2', '李四', '1');
INSERT INTO `student` VALUES ('3', '王五', '1');
INSERT INTO `student` VALUES ('4', '溜溜', '1');

1.2、实体类

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

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

1.2、按照查询嵌套处理

  

    <resultMap id="studentAndTeacher" type="student">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        
        <association property="teacher" column="tid" javaType="teacher" select="getTeacher"/>

    resultMap>
    <select id="getAllStudents" resultMap="studentAndTeacher">
        select * from student
    select>
    <select id="getTeacher" resultType="teacher">
        select * from teacher where id = #{id}
    select>

1.3、按照结果嵌套处理(联表查询 )

        注意点:就是当要联立的两张表都有相同的字段名时,会起“冲突”,导致查询的结果都是前面一张表的字段属性值
        解决思路:在编写sql语句时,给冲突的列起别名,然后在resultMap结果集映射中利用别名可以有效的避免冲突。


    <select id="getAllStudents2" resultMap="studentMap">
        select s.id sid,s.name sname, t.name tname, t.id t_id from student s,teacher t where s.tid=t.id
    select>
    <resultMap id="studentMap" type="student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <association property="teacher" javaType="teacher">
            <result property="id"  column="t_id"/>
            <result property="name" column="tname"/>
        association>
    resultMap>

2、一对多的处理(Collection)

例如:一个老师拥有多个学生
对于老师而言就是一个一对多的处理。
MyBatis学习笔记--下篇_第2张图片

2.1、实体类

public class Teacher {
    private int id;
    private String  name;
    //一个老师拥有多个老师
    List<Student> students;
}
public class Student {
    private int id;
    private String name;
    //学生只有一个老师
    private int tid;
}

2.2、按结果查询(联表查询)

    <!--    按照结果嵌套查询-->
    <resultMap id="teacherMap" type="teacher">
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
        <!--
                复杂的属性:单独处理。 我们需要单独处理的是 对象:association 集合:collection
                javaType=“”  填的都是指定属性的类型
                在集合中一般都是泛型信息,使用ofType获取
         -->
        <collection property="students" ofType="student">
            <result property="id" column="sid"/>
            <result property="name" column="sname"/>
            <result property="tid" column="tid"/>
        </collection>
    </resultMap>

    <select id="getAllTeacher" resultMap="teacherMap">
        select s.id sid,s.name sname,t.id tid,t.name tname
        from teacher t,student s where s.tid=t.id
    </select>

2.3、按照结果嵌套查询


    <select id="getTeacher2" resultMap="teacherMap2">
        select * from teacher where id=#{id}
    select>
    <resultMap id="teacherMap2" type="teacher">
        <collection property="students"  javaType="ArrayList"  ofType="student" column="id" select="getStudentByTeacher_id">
            
        collection>
    resultMap>
    <select id="getStudentByTeacher_id" parameterType="int" resultType="student">
        select * from student where tid=#{tid}
    select>

总结:

  • 关联—【association】–多对一
  • 集合—【collection 】一对多
  • javatype &ofType
    • javatype :用来指定实体类中属性的类型
    • ofType:用来映射到List或者集合的破击类型,泛型中的约束类型!

注意点:

  • 保证sql的可读性,尽量保证sql的通俗易通
  • 注意一对多和多对一中,属性名和字段的问题!
  • 如果问题不好排查,可以使用日志,建议使用log4j

3、动态Sql

动态Sql:就是指根据不同的条件生成不同的SQL语句

3.1、表的创建

DROP TABLE IF EXISTS `blog`;
CREATE TABLE `blog` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '主键',
  `title` varchar(255) DEFAULT NULL COMMENT '博客标题',
  `author` varchar(255) DEFAULT NULL COMMENT '博客作者',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `views` int DEFAULT NULL COMMENT '浏览量',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='博客表';

-- ----------------------------
-- Records of blog
-- ----------------------------
INSERT INTO `blog` VALUES ('1', '游戏', 'randan', '2023-06-30 17:22:07', '1');
INSERT INTO `blog` VALUES ('2', '日志', 'radan', '2023-10-30 17:21:21', '22');
INSERT INTO `blog` VALUES ('3', '兰州牛肉面', 'js', '2023-06-30 17:21:20', '33');

3.2、动态sql中三大标签的使用

3.2.1、if

<select id="queryBlog" parameterType="map" resultType="blog">
        select * from blog where 1=1
        <if test="title != null ">
            and title=#{title}
        if>
        <if test="author != null ">
            and author=#{author}
        if>
    select>

3.2.2、choose(when,otherwise)

这个标签像Switch,Case语法的使用规则。(模糊查找,提供了什么就查询这些条件符合的记录)。
只能选择一个 按照顺序来判断

<select id="queryBlogChoose2" parameterType="map" resultType="blog">
        
        select * from blog
        <where>
          <choose>
              
              <when test="title !=null">
                  title=#{title}
              when>
              <when test="author != null">
                  and author=#{author}
              when>
              <otherwise>
                  and views =#{views}
              otherwise>
          choose>
        where>
    select>

3.2.3、 trim(where,set)

  • where:where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除
  • set:set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。

Where

    <select id="queryBlogChoose" parameterType="map" resultType="blog">

        select * from blog
        <where>
            <if test="title != null ">
                and title=#{title}
            if>
            <if test="author != null ">
                and author=#{author}
            if>
        where>

MyBatis学习笔记--下篇_第3张图片

Set(注意:必须有一个if成立,不然就会导致update语句后面没有set更新的值。)

 <update id="updateBlog" parameterType="map" >
        update blog
        <set>
            <if test="title != null">title=#{title},if>
            <if test="author != null">author=#{author}if>
        set>
        where id=#{id}
    update>

3.2.4、 for each

MyBatis学习笔记--下篇_第4张图片

    <select id="queryBlogForEach" parameterType="map" resultType="blog">

        select * from blog
        <where>
            <foreach collection="ids" index="index" item="id" open=" and ("  close=")" separator="or">
                 id =  #{id}
             foreach>
        where>

测试:

  @org.junit.Test
    public void test006() throws IOException {
        SqlSession sqlSession= MyBatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        HashMap map=new HashMap();
        List<Integer> ids=new ArrayList<Integer>();
        ids.add(1);
        ids.add(2);
        ids.add(3);
        map.put("ids",ids);
        System.out.println(mapper.queryBlogForEach(map));
        sqlSession.commit();
        sqlSession.close();
    }

3.2.5、 Sql片段

作用:将重复的SQL语句抽取出来,放到标签中,可以进行复用。

  1. 使用sql标签抽取公共部分
   <sql id="title-author">
        <if test="title != null ">
            and title=#{title}
        if>
        <if test="author != null ">
            and author=#{author}
        if>
    sql>
  • 使用include标签引用公共部分定义的sql片段
    <select id="queryBlog" parameterType="map" resultType="blog">
        select * from blog where 1=1
        
        <include refid="title-author">include>
       
    select>

注意事项:

  • 最好基于单表来定义sql片段
  • sql抽取的部分不要存在where标签

4、缓存

4.1、简介

查询数据–>连接数据库,好资源!

什么是缓存[Cache]?

  • 存在内存中的临时数据。
  • 将用户经常查询的数据放在缓存(内存)中,用户查询数据就不用偶从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
    为什么使用缓存?
  • 减少和数据库的交互次数,减少系统开销,提高系统效率。

什么样的数据能使用缓存

  • 经常查询并且不经常改变的数据。

4.2、Mybatis缓存

  • MyBatis 包含了一个非常强大的查询缓存特性,他可以非常方便地定制和配置缓存,缓存可以极大的提升查询效率。

  • MyBatis 系统中默认定义了两级缓存:以及缓存和二级缓存

    • 默认情况下,只有以及缓存开启。(SQLSession级别的缓存,也称为本地缓存)
    • 二级缓存需要手动开启和设置,它是基于namespace级别的缓存。为了提高扩展性,MyBatis定义了缓存接口Cache 。我们可以通过缓存Cache接口来定义二级缓存。

4.3、一级缓存(作用小)

测试步骤:

  • 开启日志
    <settings>
        
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    settings>
  • 测试(在一个session中查询两次相同操作)
    @Test
    public void  test001() throws IOException {
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> allUsers = mapper.getAllUsers();
        List<User> users = allUsers;
        System.out.println(users==allUsers);
        sqlSession.close();
    }

MyBatis学习笔记--下篇_第5张图片
缓存失效的情况:

  1. 查询不同的条件
  2. 增删改操作,可能会改变原来的啥时间,所以必定会刷新缓存!
  3. 查询不同的Mapper.xml
  4. 手动清理缓存!
sqlSession.clearCache();//清理缓存

小结:一级缓存默认是开启的,只在一次SQLSession中有效,也就是拿到连接到关闭连接的区间。

4.4、二级缓存

  • 二级缓存也叫全局缓存,一级缓存作用域太低了,所以产生了二级缓存
  • 基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
  • 工作机制
    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
    • 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保持在二级缓存中;
    • 新的会话查询信息,就可以从二级缓存中获取内容;
    • 不同的mapper查出的数据会放在自己对应的缓存(map)中;

步骤:

  • 在mybatis配置文件中,配置全局缓存设置;
 <settings>
        
        <setting name="cacheEnabled" value="true"/>
    settings>
  • 在mapper.xml加缓存标签

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

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

  • 测试
@Test
    public void  test002() throws IOException {
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        SqlSession sqlSession1 = MyBatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        User user1 = mapper.findUserById(1);
        sqlSession.close();
        UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
        User user2 = mapper1.findUserById(1);
        System.out.println(user1==user2);
        sqlSession1.close();
    }

注意点:
二级缓存存在于 SqlSessionFactory 生命周期中

我一直以为二级缓存针对的对象是一个Mapper对象,只要是针对同一个Mapper的操作,都可以实现二级缓存。但是前提是操作必须在同一个SqlSessionFactory 中进行。

在一开始的代码中,通过调用三次getFactory()并打开session,实例化了三个不同的SqlSessionFactory 对象,这样在后续的SQL操作中,是不可能命中二级缓存的。

MyBatis学习笔记--下篇_第6张图片

4.5、缓存原理

MyBatis学习笔记--下篇_第7张图片

4.6、自定义缓存–Ehcache

Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存。

要在程序中使用,先导入依赖


<dependency>
    <groupId>org.mybatis.cachesgroupId>
    <artifactId>mybatis-ehcacheartifactId>
    <version>1.2.1version>
dependency>

在mapper.xml中使用对应的缓存即可

   <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

编写ehcache.xml文件,如果在 加载时 未找到 /ehcache.xml 资源或出现问题,则将使用默认配置。


<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false" dynamicConfig="false">
    
    <diskStore path="./tmpdir/Tmp_EhCache"/>



    <defaultCache
            eternal="false"
            maxElementsInMemory="10000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="259200"
            memoryStoreEvictionPolicy="LRU"/>
    <cache
            name="cloud_user"
            eternal="false"
            maxElementsInMemory="5000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="1800"
            memoryStoreEvictionPolicy="LRU"/>
    
    
ehcache>

测试结果还是和之前一样的。

5、问题

问题描述:当我们在mybatis的配置文件中,写好绑定Mapper.xml文件时,运行结果依旧提示找不到Mapper.xml或是没有绑定。
MyBatis 配置信息:
MyBatis学习笔记--下篇_第8张图片

错误信息:
MyBatis学习笔记--下篇_第9张图片
此时,我们可以查看target目录下的class文件夹,发现并没有编译出UserMapper.xml文件。
MyBatis学习笔记--下篇_第10张图片
解决方法:主要原因就是Maven在导出资源失败,资源过滤出现问题。我们需要在pom.xml中添加防止一些指定的资源文件的过滤配置。

       
    <build>
        <resources>
            <resource>
                <directory>src/main/resourcesdirectory>
                <includes>
                    <include>**/*.propertiesinclude>
                    <include>**/*.xmlinclude>
                includes>
                <filtering>truefiltering>
            resource>
            <resource>
                <directory>src/main/javadirectory>
                <includes>
                    <include>**/*.propertiesinclude>
                    <include>**/*.xmlinclude>
                includes>
                <filtering>truefiltering>
            resource>
        resources>
    build>

你可能感兴趣的:(MyBatis,mybatis,学习,笔记)