KS1_S05_Mybatis详解笔记

2021-06-06_KS1_S05_Mybatis详解笔记

一、基础环境

  • JDK 1.8
  • MySQL 5.7
  • Maven 3.6.1
  • IDEA
  • 回顾知识点
    • JDBC
    • MySQL
    • Java基础
    • Maven
    • junit
  • 官网地址:https://blog.mybatis.org/mybatis-3/index.html
  • Java里面操作xml文件——dom4j

二、第一个MyBatis程序

思路:搭建环境——》导入MyBatis——》编写代码——》测试!

2.1、搭建环境

  1. 搭建数据库

    CREATE DATABASE mybatis;
    
    USE mybatis;
    
    
    CREATE TABLE user (
    	id  	INT(20) 		NOT NULL,
        name	VARCHAR(30)		DEFAULT NULL,
        pwd		VARCHAR(30)		DEFAULT NULL,
        PRIMARY KEY(id)
    ) ENGINE=INNODB default CHARSET=utf8;
    
    
    INSERT INTO user (id,name,pwd) VALUES
    (1,'狂神','123456'),
    (2,'张三','123456'),
    (3,'李四','123456');
    
    
    SELECT * FROM mybatis.user;
    

2.1、搭建环境

  1. 12312

2.3、编写代码

  1. 实体类
  2. Dao接口
  3. 接口实现类

2.4、可能遇到的问题:

  1. 配置文件没有注册
  2. 绑定接口错误
  3. 方法名不对
  4. 返回类型不对
  5. Maven导出资源问题

三、CRUD

1、namespace

  1. namespace中的包名要和 DAO/mapper 接口的包名一致

2、select

  1. id,就是对应namespace 中的方法名
  2. resultType,Sql 语句执行的返回值,class、基本类型
  3. parameterType,

3、insert

4、update

5、delete

6、万能Map

  1. 假设实体类、或者数据库中的表字段、参数过多,我们应当考虑使用Map。
  2. Map传递参数,直接在sql中取出key即可——【parameterType = “map"】
  3. 对象传递参数,直接在sql中取对象的属性即可——【parameterTyep=“object”】
  4. 只有一个基本类型参数的情况下,可以直接在sql中取到
  5. 多个参数用Map,或者注解。

7、模糊查询:

  1. Java代码执行的时候,传递通配符%%
  2. 在sql拼接中使用通配符——避免SQL注入

四、配置解析

1、核心配置文件

  1. mybatis-config.xml
    1. dataSource,连接数据库:dbcp、c3p0、druid、hyki*
    2. transactionManager type:JDBC、managed
    3. type=UNPOOLED、POOPLED、JNDI
2、环境配置(environment)
  1. MyBatis 可以配置成适应多种环境
  2. 不过要注意:尽管可以配置成多个环境,但每个 SqlSessionFactory 实例只能选择其中一种环境
  3. 需要学会使用 配置多套运行环境
  4. Mybatis 默认的事务管理器就是:JDBC,默认的连接池为:POOLED
3、属性(properties)
  1. 可以直接引入外部文件
  2. 可以在其中增加一些属性配置
  3. 如果连个文件有同一个字段,优先使用外部配置文件的。(覆盖)
4、类型别名 (typeA)
  1. 第一种方式:
    1. 类型别名是为Java类型设置一个短的名字
    2. 存在的意义仅仅用于减少类完全限定名的冗余
  2. 第二种方式:
    1. 也可以指定一个包名,MyBatis会在包名下搜索对应的Java Bean。
    2. 扫描实体类的包,它的默认别名就为这个类的类名,首字母小写。
    3. 若有注解名,则为注解名

5、设置(settings)

  1. cacheEnabled,缓存开启
  2. lazyLoadingEnabled,懒加载
  3. logimpl,指定MyBatis的日志具体实现

6、其他配置

  1. typeHandler(类处理器)
  2. objectFactory(对象工厂)
  3. 插件(plugins)

7、映射器(mappers)

  1. MapperRegistry:注册绑定我们的Mapper文件

    1. 方式一:【推荐使用

      <!--每一个Mapper.XML都需要在Mybatis核心配置文件中注册!-->
      <mappers>
      	<mapper resource="com/zhout/dao/UserMapper.xml"/>
      </mappers>
      
    2. 方式二:使用class文件绑定注册

      <!--每一个Mapper.XML都需要在Mybatis核心配置文件中注册!-->
      <mappers>
      	<mapper class="com.zhout.dao.UserMapper"></mapper>
      </mappers>
      
      • 注意点:
        • 接口和它的Mapper配置文件必须同名
        • 接口和他的Mapper配置文件必须在同一个包下
    3. 方式三:使用扫描包进行注入绑定

      <!--每一个Mapper.XML都需要在Mybatis核心配置文件中注册!-->
      <mappers>
      	<package name="com.zhout.dao"/>
      </mappers>
      

8、作用域(Scope)和生命周期

1、SqlSessionFactoryBuilder

  1. 一旦创建SqlSessionFactory,就不需要它了
  2. 局部变量

2、SqlSessionFactory

  1. 可以认为是:数据库连接池
  2. 一旦创建,就一直存在,没有理由丢弃它或重新创建另一个实例
  3. 最佳作用域时应用作用域
  4. 最简单的使用是单例模式或者静态单例模式

3、SqlSession

  1. 连接到连接池的一个请求

  2. SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳作用域时请求或者方法作用域。

  3. 用完之后赶紧关闭,否则资源被占用。(并发情况下,不还容易宕机)

五、解决属性名和字段名不一致的问题

数据库中的字段和类名字段不一致

1、起别名

<select id="getUserById" parameterType="int" resultType="com.zhout.pojo.User">
        SELECT id,name,pwd as password FROM mybatis.user WHERE id = #{id};
</select>

2、resultMap,结果集映射

<!--结果集映射-->
<resultMap id="UserMap" type="User">
    <!--column为数据库中的字段,property实例类中的属性-->
    <result column="id" property="id"/>
    <result column="name" property="name"/>
    <result column="pwd" property="password"/>
</resultMap>

<select id="getUserById" parameterType="int" resultMap="UserMap">
    SELECT id,name,pwd FROM mybatis.user WHERE id = #{id};
</select>

六、日志

1、日志工厂

  1. 如果一个数据库操作,出现了异常,需要排错时:日志就是最好的助手
  2. 曾经:sout、dubug
  3. 现在:日志工厂,logImpl
    1. SLF4J
    2. LOG4J【需要掌握】
    3. LOG4J2
    4. JDK_LOGGIG
    5. COMMONS_LOGGING
    6. STDOUT_LOGGING【需要掌握】
    7. NO_LOGGING
  4. 在Mybatis 中具体使用哪个日志实现,在设置中设置

2、STDOUT_LOGGING

  1. 是标准日志输出
  2. 配置好,可以直接用

3、LOG4J

  1. 什么是LOG4J,百度百科

  2. 可以控制每一条日志的数据格式

  3. 可以定义每一条日志信息的级别,能够更加细致地控制日志的生成 过程

  4. 可以通过一个配置文件来进行灵活地配置,而不需要修改应用代码。

  5. 使用过程:

    1. 导入 log4j 的包

    2. 配置log4j.properties文件

      # log4j.rootLogger设置日志输出类别和级别:只输出不低于该级别的日志信息: DEBUG < INFO < WARN < ERROR < FATAL
      # DEBUG:日志级别     console:输出位置自己定义的一个名字       file:输出位置自己定义的一个名字
      log4j.rootLogger=DEBUG,console,file
      
      #############
      # 输出到控制台
      #############
      
      # 配置CONSOLE输出到控制台
      log4j.appender.console=org.apache.log4j.ConsoleAppender
      log4j.appender.console.Target = System.out
      log4j.appender.console.Threshold = DEBUG
      # 配置CONSOLE设置为自定义布局模式
      log4j.appender.console.layout=org.apache.log4j.PatternLayout 
      # 配置CONSOLE日志的输出格式  [frame] 2019-08-22 22:52:12,000  %r耗费毫秒数 %p日志的优先级 %t线程名 %C所属类名通常为全类名 %L代码中的行号 %x线程相关联的NDC %m日志 %n换行
      log4j.appender.console.layout.ConversionPattern=[frame] %d{yyyy-MM-dd HH:mm:ss,SSS} - %-4r %-5p [%t] %C:%L %x - %m%n
      
      ################
      # 输出到日志文件中
      ################
      
      # 配置logfile输出到文件中 文件大小到达指定尺寸的时候产生新的日志文件
      log4j.appender.file=org.apache.log4j.RollingFileAppender
      # 保存编码格式
      log4j.appender.file.Encoding=UTF-8
      # 输出文件位置此为项目根目录下的logs文件夹中
      log4j.appender.file.File=./logs/zhout.log
      # 后缀可以是KB,MB,GB达到该大小后创建新的日志文件
      log4j.appender.file.MaxFileSize=10MB
      log4j.appender.file.Threshold=DEBUG
      # 设置滚定文件的最大值3 指可以产生root.log.1、root.log.2、root.log.3和root.log四个日志文件
      # log4j.appender.file.MaxBackupIndex=3
      # 配置logfile为自定义布局模式
      log4j.appender.file.layout=org.apache.log4j.PatternLayout
      # 配置CONSOLE日志的输出格式  [frame] 2019-08-22 22:52:12,000  %r耗费毫秒数 %p日志的优先级 %t线程名 %C所属类名通常为全类名 %L代码中的行号 %x线程相关联的NDC %m日志 %n换行
      log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %F %p %m%n
      
      ##########################
      # 输出Mybatis的日志配置信息
      ##########################
      log4j.logger.org.mybatis=DEBUG
      log4j.logger.java.sql=DEBUG
      log4j.logger.java.sql.Statement=DEBUG
      log4j.logger.java.sql.ResultSet=DEBUG
      log4j.logger.java.sql.PreparedStatement=DEBUG
      
    3. 配置Mybatis 的日志的实现为log4j

      <!--设置项目-->
          <settings>
              <setting name="logImpl" value="LOG4J"/>
          </settings>
      
    4. 测试

      @Test
      public void testLog4j(){
          logger.info("info:进入了TestLog4j");
          logger.debug("debug:进入了TestLog4j");
          logger.error("error:进入了TestLog4j");
      }
      

七、分页

1、分页的原因:

  1. 可以减少数据的处理量

2、分页的SQL语法:

SELECT * FROM user LIMIT startIndex, pageSize;

SELECT * FROM user LIMIT 2, -1;

SELECT * FROM user LIMIT 4;

3、使用Mybatis Limit 实现分页,核心SQL:

  1. 接口

    1. // 分页
      List<User> getUserByLimit(Map<String,Integer> map);
      
  2. Mapper.XML

    1.  <!--分页-->
      <select id="getUserByLimit" parameterType="map" resultMap="UserMap">
          SELECT * FROM mybatis.user LIMIT #{startIndex},#{pageSize};
      </select>
      
  3. 测试

    1. // 分页
      @Test
      public void getUserByLimit(){
          SqlSession sqlSession = MybatisUtils.getSqlSession();
          UserMapper mapper = sqlSession.getMapper(UserMapper.class);
      
          HashMap<String, Integer> map = new HashMap<String, Integer>();
          map.put("startIndex",2);
          map.put("pageSize",2);
      
          List<User> userList = mapper.getUserByLimit(map);
      
          for (User user : userList) {
              System.out.println("分页:" +user);
          }
      
          sqlSession.close();
      }
      

4、RowBounds 分页,不再使用 SQL 实现分页

  1. 接口

    1.  // RowBounds分页
      List<User> getUserByRowBounds();
      
  2. Mapper.xml

    1. <!--RowBounds分页-->
      <select id="getUserByRowBounds" parameterType="map" resultMap="UserMap">
          SELECT * FROM mybatis.user
      </select>
      
  3. 测试

    1.  // RowBounds分页
      @Test
      public void getUserByRowBounds(){
          SqlSession sqlSession = MybatisUtils.getSqlSession();
      
          // RowBounds 实现
          RowBounds rowBounds = new RowBounds(1, 2);
      
          // 通过 Java 代码层面实现分页
          List<User> userList = sqlSession.selectList("com.zhout.dao.UserMapper.getUserByRowBounds",null,rowBounds);
      
          for (User user : userList) {
              System.out.println(user);
          }
          
          sqlSession.close();
      }
      

5、分页插件——PageHelper

  1. 地址:https://pagehelper.github.io/


八、使用注解开发

1. 接口的作用

  1. 解耦
  2. 可扩展
  3. 提高复用

2. 关于接口的理解

  1. 接口从更深层次的理解,应是定义(规范、约束)与实现(名实分离的原则)的分离
  2. 接口的本身反映了系统 设计人员对系统的抽象理解
  3. 接口应有两类:
    1. 第一类是对 一个个体的抽象,它可对应为一个抽象提(abstract class)
    2. 第二类是对一个个体某个方面的抽象,即形成一个抽象面(interface)
  4. 一 个体有可能有多个抽象面,抽象体与抽象面是有区别的

3. 三个面向的区别

  1. 面向对象:指的是我们考虑问题时,以对象为单位,考虑它的属性及方法
  2. 面向过程:指的是我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现
  3. 面向接口:指的是接口设计与非接口设计,是针对复用技术而言的,与面对对象(过程)不是一个问题,更多的体现就是对系统整体的架构

4. 内容:

  1. 注解在接口上实现

    1. @Select("SELECT id,name,pwd as password FROM user")
      List<User> getUsers();
      
  2. 需要在核心配置文件中绑定接口

    1. <!--绑定接口-->
      <mappers>
          <mapper class="com.zhout.dao.UserMapper"/>
      </mappers>
      
  3. 测试

5. 实现机制

  1. 本质:反射机制实现
  2. 底层:动态代理

6. Mybatis 详细的执行流程

  1. [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2ZBy1UIZ-1622949364087)(KS1_Step05_MyBatis详解.assets/1622949223756.png)]

7.使用注解来CRUD

  1. 我们可以在工具类创建的时候实现自动提交事务

    1. // SqlSession 完全包含了面向数据库执行 SQL 命令 所需的所有方法
      public static SqlSession getSqlSession(){
      	return sqlSessionFactory.openSession(true);
      }
      
  2. 编写接口,增加注解

    1. // 方法存在多个参数,所有的参数前面都要加上 @Parm("id") 注解
      @Select("SELECT id,name,pwd as password  FROM user where id = #{id}")
      User getUserByID(@Param("id") int id);
      
  3. 【重点注意】:我们必须将接口注册绑定到我们的核心配置文件中

    1. <!--绑定接口-->
      <mappers>
      	<mapper class="com.zhout.dao.UserMapper"/>
      	<!--没有使用接口的话,可以扫描-->
      	<mapper resource="com/zhout/dao/*Mapper.xml"/>
      </mappers>
      
  4. 关于 @Param(“id”)注解:

    1. 基本数据类型的参数或者String类型,需要加上

    2. 引用类型的不需要添加

    3. 如果只有一个基本类型的话,可以忽略,但是建议 都加上

    4. 我们在SQL中引用的就是我们这里的@Param()中设定的属性名

      1. #{}——能够很大程度上防止sql注入
        ${}——无法防止sql注入
        
        注意:Mybatis排序时,使用order by 动态参数时需要注意,需要使用${}而不是使用#{}
        

九、Lombok

1. 解释其是啥:

  1. java library
  2. plugins
  3. build tools
  4. with one annotation your class

2. 使用步骤

  1. 相关注解

    1. @Getter and @Setter
      @FieldNameConstants
      @ToString
      @EqualsAndHashCode
      @AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
      @Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
      @Data
      @Builder
      @SuperBuilder
      @Singular
      @Delegate
      @Value
      @Accessors
      @Wither
      @With
      @SneakyThrows
      @val
      @var
      experimental @var
      @UtilityClass
      
  2. 常用的Lombok注解

    1. @Data:无参构造、getset、toString、hashCode、equals
      @AllArgsConstructor
      @NoArgsConstructor
      @EqualsAndHashCode
      

十、多对一处理

1. 多对一

  1. 多个学生对应一个老师
  2. 对于学生这边而言,关联 :多个学生,关联一个老师【多对一】
  3. 对于老师而言,集合:一个老师,有多个学生【一对多】

2. SQL

  1. 表结构

    1. [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qUqMrU0J-1622949364089)(KS1_Step05_MyBatis详解.assets/1622871811154.png)]
  2. 按照查询嵌套处理:

    1. <!--思路:1、 查询所有的学生信息;2、 根据查询出来的学生的tid,寻找对应的老师——子查询-->
      <select id="getStudentList" resultMap="StudentTeacher">
          SELECT * FROM student
      </select>
      
      <resultMap id="StudentTeacher" type="Student">
          <result property="id" column="" />
          <result property="name" column="" />
      	<!--复杂的属性需要单独处理, 对象: association, 集合: collection-->
          <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
      </resultMap>
      
      <select id="getTeacher" resultType="Teacher">
          SELECT * FROM teacher WHERE id=#{id}
      </select>
      
  3. 按照结果嵌套处理:

    1. <!--思路二(按照结果嵌套处理):-->
      <select id="getStudentList2" resultMap="StudentTeacher2">
          SELECT s.id sid ,s.name sname,t.name tname FROM student s,teacher t WHERE s.tid = t.id;
      </select>
      
      <resultMap id="StudentTeacher2" type="Student">
          <result property="id" column="sid"/>
          <result property="name" column="sname"/>
          <association property="teacher" javaType="Teacher">
              <result property="name" column="tname"/>
          </association>
      </resultMap>
      
  4. 回顾Mysql 多对一查询方式:

    1. 子查询
    2. 链表查询

十一、一对多处理

  1. 一个老师拥有多个学生

    1. 对于老师而言,就是一对多的关系
  2. 按照结果嵌套处理

    1. <!--按照结果嵌套处理-->
      <select id="getTeacher" parameterType="int" resultMap="TeacherStudent">
          SELECT s.id  sid, s.name  sname, t.name tname, t.id tid
          FROM student s, teacher t
          WHERE s.tid = t.id
          and t.id=#{tid};
      </select>
      
      <resultMap id="TeacherStudent" type="Teacher">
          <result property="id" column="tid"/>
          <result property="name" column="tname"/>
          <!--复杂的属性需要单独处理, 对象: association; 集合: collection;
                  javaType=""s为指定的属性类型
                  集合中的泛型信息,我们使用ofType 获取
              -->
          <collection property="students" ofType="Student">
              <result property="id" column="sid"/>
              <result property="name" column="sname"/>
              <result property="tid" column="tid"/>
          </collection>
      </resultMap>
      
  3. 按照查询嵌套处理

    1. <!--按照查询嵌套处理-->
      <select id="getTeacher2" resultMap="TeacherStudent2">
          SELECT * FROM teacher WHERE id=#{tid};
      </select>
      
      <resultMap id="TeacherStudent2" type="Teacher">
          <result property="id" column="id"/>
          <result property="name" column="name"/>
          <collection property="students" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId" column="id"/>
      </resultMap>
      
      <select id="getStudentByTeacherId"  resultType="Student">
          SELECT * FROM student WHERE tid=#{tid}
      </select>
      

4. 小结

  1. 关联 - association 【多对一】
  2. 集合 - collection 【一对多】
  3. javaType & ofType
    1. javaType: 用来指定实体类中属性的类型
    2. ofType: 用来指定映射到 List 或者集合中的 pojo 类型,泛型中的约束类型
  4. 注意点】:
    1. 保证SQL 的可读性,尽量保证通俗易懂
    2. 注意一对多和多对一中的属性名和字段的问题
    3. 如果问题不好排查错误,可以使用日志,建议使用Log4j
  5. 慢SQL:1s - 1000s
    1. MySQL引擎:InnoDB、MyISAM的区别
    2. InnoDB底层原理:
    3. 索引:为什么要使用索引、索引的结构、索引的分类、如何去操作
    4. 索引优化:

十二、动态 SQL

1、什么是动态SQL

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

    1. if
      choose (when,  otherwise)
      trim (where, set)
      foreach
      

2、搭建环境

  1. 创建数据库表:

    CREATE TABLE bolg (
    	id			VARCHAR(50)		NOT NULL COMMENT '博客id',
        title		VARCHAR(100)	NOT NULL COMMENT '博客标题',
        author		VARCHAR(30)		NOT NULL COMMENT '博客作者',
        create_time	DATETIME		NOT	NULL COMMENT '创建时间',
        views		INT(30)			NOT NULL COMMENT '浏览器'
    ) ENGINE=InnoDB  DEFAULT CHARSET=UTF8;
    
  2. 基础步骤

    1. 导包
    2. 编写配置文件
    3. 编写实体类
    4. 编写实体类对应 Mapper接口 和 Mapper.xml文件
    5. 编写测试类

3、IF

<select id="queryBlogIF" 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>

4、choose

<select id="queryBlogChoose" parameterType="map" resultType="Blog">
    SELECT * FROM blog
    <where>
        <choose>
            <when test="title != null">
                and title=#{title}
            </when>
            <when test="author != null">
                and author=#{author}
            </when>
            <otherwise>
                and views=#{views}
            </otherwise>
        </choose>
    </where>
</select>

5、set

<update id="updateBlogSet" parameterType="map">
    UPDATE blog
    <set>
        <if test="title != null">
            title = #{title},
        </if>
        <if test="author != null">
            author = #{author},
        </if>
    </set>
    WHERE id = #{id}

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

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

6、小结

所谓的动态SQL,本质还是SQL语句,只是我们可以在SQL层面,去执行一个逻辑代码

7、Foreach

SELECT * FROM user WHERE 1=1 and (id=1 or id=2 or id=3);

<select id="queryBlogForeach" parameterType="map" resultType="Blog">
    SELECT * FROM blog
    <where>
        <foreach collection="ids" item="id"
                 open="and (" separator="or" close=")">
            id = #{id}
        </foreach>
    </where>
</select>

8、SQL片段

  1. 有的时候,可能会将一些功能的部分抽取出来,方便复用 。

    1. 使用SQL 标签抽取公共的部分

    2. 在需要使用的地方使用 Include 标签引用即可

      1. <select id="queryBlogIF" parameterType="map" resultType="Blog">
            SELECT * FROM blog
            <where>
                <include refid="if-title-author"></include>
            </where>
        </select>
        
        <sql id="if-title-author">
            <if test="title != null">
                and title=#{title}
            </if>
            <if test="author != null">
                and author=#{author}
            </if>
        </sql>
        
  2. 注意事项

    1. 最好基于单表来定义SQL片段
    2. 不要存在 where 标签

9、动态SQL-小结

动态SQL 就是在拼接SQL语句,我们只要保证SQL的正确性,按照SQL 的格式,去排列组合。

【建议】:

  1. 先在MySQL中写出完整的SQL语句,再对应地去修改成为我们的动态SQL,从而实现通用。

十三、缓存

1、简介

查询,连接数据库,耗资源
一次查询的结果,给他存在一个可以直接取到的地方——》内存:缓存

我们再次查询相同数据的时候,直接走缓存,就不用走数据库了
  1. 什么是缓存

    1. 存在内存中的临时数据
    2. 将 用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)内查询了。改为从缓存中查询,从而提高 查询效率,能解决高并发系统的性能问题。
  2. 为什么要使用缓存

    1. 减少和数据库的交互次数,减少系统开销,提高系统效率。
  3. 什么样的数据能使用缓存

    1. 经常查询并且不经常改变的数据
  4. 系统三高:高性能、高并发、高可用

  5. 数据库原则:原子性、事务性、隔离性

    1. 读写分离
      1. 主从复制:三写,一读

2、Mybatis缓存

  1. Mybatis 包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
  2. Mybatis 系统中默认定义了两级缓存:一级缓存二级缓存
    1. 默认情况下,只有一级缓存开启和配置。(SqlSession 级别的缓存,也称为本地缓存)
    2. 二级缓存需要手动开启和配置,他是基于namespace 级别的缓存
    3. 为了提高扩展性,Mybatis 定义了缓存接口Cache。我们可以通过实现Cache 接口来自定义二级缓存

3、一级缓存

  1. 一级缓存也叫本地缓存——SqlSession
    1. 与数据库同一次会话期间查询到的数据会放在本地缓存中
    2. 以后 如果需要获取相同的数据,直接从缓存中拿,没有必要再去查询数据库
  2. 一级缓存 - 缓存失效的情况:
    1. 查询不同的东西
    2. 增删改操作,可能会改变原来的数据 ,所以必定会刷新缓存
    3. 查询不同的Mapper.xml ——此种情况,二级缓存也会失效
    4. 手动清理缓存
  3. 小结:
    1. 一级缓存默认是开启的,只在一次 SqlSession 中有效,也就是”拿到连接“到”关闭连接“的这个区间
    2. 一级缓存就是一个map

4、二级缓存

  1. 二级缓存也叫全局缓存,一级缓存作用域太低,所以诞生了二级缓存

  2. 基于namespace 级别的缓存,一个名称空间,对应一个二级缓存

  3. 工作机制:

    1. 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中
    2. 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了 ,一级缓存中的数据被保存到二级缓存中
    3. 新的会话查询信息,就可以从二级缓存中获取内容
  4. 使用步骤:

    1. 开启全局缓存

      1. <!--显示的开启全局缓存-->
        <setting name="cacheEnabled" value="true"/>
        
    2. 在要使用二级缓存的Mapper.xml中开启:

      1. 增加cache标签

        1. <!--在当前Mapper.xml中使用二级缓存,设置:先进先出、60秒刷新、512个、只读-->
          <cache />
          
      2. 也可以 自定义参数

        1. <!--在当前Mapper.xml中使用二级缓存,设置:先进先出、60秒刷新、512个、只读-->
          <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
          
    3. 我们需要将实体类序列化 ,否则就会报错。

  5. 小结

    1. 只要开启了二级缓存,在同一个Mapper.xml下就有效
    2. 所有的数据都会先放在一级缓存中
    3. 只有当会话提交,或者关闭的时候,才会提交到二级缓存中

5、Mybatis 缓存原理

  1. 先看二级缓存中有没有
  2. 再看一级缓存中有没有
  3. 查询数据库

6、自定义缓存——ehcache

简介:

  1. Ehcache 是一个纯Java 的进程内缓存框架,具有快捷、精干等特点,是Hiberrnate 中默认的CacheProvider。
  2. Ehcache 是一种广泛使用的开源Java 分布式缓存。
  1. 先导包

    1.  <!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
      <dependency>
          <groupId>org.mybatis.caches</groupId>
          <artifactId>mybatis-ehcache</artifactId>
          <version>1.2.1</version>
      </dependency>
      
  2. 在mybaits-config.xml中启用全局缓存

    1.  <!--设置项目-->
      <settings>
          <!--显示的开启全局缓存-->
          <setting name="cacheEnabled" value="true"/>
      </settings>
      
  3. 在Mapper.xml中指定使用ehcache 缓存实现。

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

    1. <?xml version="1.0" encoding="UTF-8"?>
      <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
               updateCheck="false">
          <!--
             diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:
             user.home – 用户主目录
             user.dir  – 用户当前工作目录
             java.io.tmpdir – 默认临时文件路径
           -->
          <diskStore path="./tmpdir/Tmp_EhCache"/>
          <!--
             defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
           -->
          <defaultCache
                  eternal="false"
                  maxElementsInMemory="10000"
                  overflowToDisk="false"
                  diskPersistent="false"
                  timeToIdleSeconds="1800"
                  timeToLiveSeconds="259200"
                  memoryStoreEvictionPolicy="LRU"/>
          <!--
            name:缓存名称。
            maxElementsInMemory:缓存最大数目
            maxElementsOnDisk:硬盘最大缓存个数。
            eternal:对象是否永久有效,一但设置了,timeout将不起作用。
            overflowToDisk:是否保存到磁盘,当系统当机时
            timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
            timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
            diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
            diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
            diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
            memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
            clearOnFlush:内存数量最大时是否清除。
            memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
            FIFO,first in first out,这个是大家最熟的,先进先出。
            LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
            LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
         -->
          <cache
                  name="cloud_user"
                  eternal="false"
                  maxElementsInMemory="5000"
                  overflowToDisk="false"
                  diskPersistent="false"
                  timeToIdleSeconds="1800"
                  timeToLiveSeconds="1800"
                  memoryStoreEvictionPolicy="LRU"/>
      
      
      </ehcache>
      
  5. 后续需要使用Redis 数据库来做缓存,K-V键

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值