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、搭建环境
-
搭建数据库
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、搭建环境
- 12312
2.3、编写代码
- 实体类
- Dao接口
- 接口实现类
2.4、可能遇到的问题:
- 配置文件没有注册
- 绑定接口错误
- 方法名不对
- 返回类型不对
- Maven导出资源问题
三、CRUD
1、namespace
- namespace中的包名要和 DAO/mapper 接口的包名一致
2、select
- id,就是对应namespace 中的方法名
- resultType,Sql 语句执行的返回值,class、基本类型
- parameterType,
3、insert
4、update
5、delete
6、万能Map
- 假设实体类、或者数据库中的表字段、参数过多,我们应当考虑使用Map。
- Map传递参数,直接在sql中取出key即可——【parameterType = “map"】
- 对象传递参数,直接在sql中取对象的属性即可——【parameterTyep=“object”】
- 只有一个基本类型参数的情况下,可以直接在sql中取到
- 多个参数用Map,或者注解。
7、模糊查询:
- Java代码执行的时候,传递通配符%%
- 在sql拼接中使用通配符——避免SQL注入
四、配置解析
1、核心配置文件
-
mybatis-config.xml
- dataSource,连接数据库:dbcp、c3p0、druid、hyki*
- transactionManager type:JDBC、managed
- type=UNPOOLED、POOPLED、JNDI
2、环境配置(environment)
- MyBatis 可以配置成适应多种环境
- 不过要注意:尽管可以配置成多个环境,但每个 SqlSessionFactory 实例只能选择其中一种环境
- 需要学会使用 配置多套运行环境
- Mybatis 默认的事务管理器就是:JDBC,默认的连接池为:POOLED
3、属性(properties)
- 可以直接引入外部文件
- 可以在其中增加一些属性配置
- 如果连个文件有同一个字段,优先使用外部配置文件的。(覆盖)
4、类型别名 (typeA)
- 第一种方式:
- 类型别名是为Java类型设置一个短的名字
- 存在的意义仅仅用于减少类完全限定名的冗余
- 第二种方式:
- 也可以指定一个包名,MyBatis会在包名下搜索对应的Java Bean。
- 扫描实体类的包,它的默认别名就为这个类的类名,首字母小写。
- 若有注解名,则为注解名
5、设置(settings)
- cacheEnabled,缓存开启
- lazyLoadingEnabled,懒加载
- logimpl,指定MyBatis的日志具体实现
6、其他配置
- typeHandler(类处理器)
- objectFactory(对象工厂)
- 插件(plugins)
7、映射器(mappers)
-
MapperRegistry:注册绑定我们的Mapper文件
-
方式一:【推荐使用】
<!--每一个Mapper.XML都需要在Mybatis核心配置文件中注册!--> <mappers> <mapper resource="com/zhout/dao/UserMapper.xml"/> </mappers>
-
方式二:使用class文件绑定注册
<!--每一个Mapper.XML都需要在Mybatis核心配置文件中注册!--> <mappers> <mapper class="com.zhout.dao.UserMapper"></mapper> </mappers>
- 注意点:
- 接口和它的Mapper配置文件必须同名
- 接口和他的Mapper配置文件必须在同一个包下
- 注意点:
-
方式三:使用扫描包进行注入绑定
<!--每一个Mapper.XML都需要在Mybatis核心配置文件中注册!--> <mappers> <package name="com.zhout.dao"/> </mappers>
-
8、作用域(Scope)和生命周期
1、SqlSessionFactoryBuilder
- 一旦创建SqlSessionFactory,就不需要它了
- 局部变量
2、SqlSessionFactory
- 可以认为是:数据库连接池
- 一旦创建,就一直存在,没有理由丢弃它或重新创建另一个实例
- 最佳作用域时应用作用域
- 最简单的使用是单例模式或者静态单例模式
3、SqlSession
-
连接到连接池的一个请求
-
SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳作用域时请求或者方法作用域。
-
用完之后赶紧关闭,否则资源被占用。(并发情况下,不还容易宕机)
五、解决属性名和字段名不一致的问题
数据库中的字段和类名字段不一致
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、日志工厂
- 如果一个数据库操作,出现了异常,需要排错时:日志就是最好的助手
- 曾经:sout、dubug
- 现在:日志工厂,logImpl
- SLF4J
- LOG4J【需要掌握】
- LOG4J2
- JDK_LOGGIG
- COMMONS_LOGGING
- STDOUT_LOGGING【需要掌握】
- NO_LOGGING
- 在Mybatis 中具体使用哪个日志实现,在设置中设置
2、STDOUT_LOGGING
- 是标准日志输出
- 配置好,可以直接用
3、LOG4J
-
什么是LOG4J,百度百科
-
可以控制每一条日志的数据格式
-
可以定义每一条日志信息的级别,能够更加细致地控制日志的生成 过程
-
可以通过一个配置文件来进行灵活地配置,而不需要修改应用代码。
-
使用过程:
-
导入 log4j 的包
-
配置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
-
配置Mybatis 的日志的实现为log4j
<!--设置项目--> <settings> <setting name="logImpl" value="LOG4J"/> </settings>
-
测试
@Test public void testLog4j(){ logger.info("info:进入了TestLog4j"); logger.debug("debug:进入了TestLog4j"); logger.error("error:进入了TestLog4j"); }
-
七、分页
1、分页的原因:
- 可以减少数据的处理量
2、分页的SQL语法:
SELECT * FROM user LIMIT startIndex, pageSize;
SELECT * FROM user LIMIT 2, -1;
SELECT * FROM user LIMIT 4;
3、使用Mybatis Limit 实现分页,核心SQL:
-
接口
-
// 分页 List<User> getUserByLimit(Map<String,Integer> map);
-
-
Mapper.XML
-
<!--分页--> <select id="getUserByLimit" parameterType="map" resultMap="UserMap"> SELECT * FROM mybatis.user LIMIT #{startIndex},#{pageSize}; </select>
-
-
测试
-
// 分页 @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 实现分页
-
接口
-
// RowBounds分页 List<User> getUserByRowBounds();
-
-
Mapper.xml
-
<!--RowBounds分页--> <select id="getUserByRowBounds" parameterType="map" resultMap="UserMap"> SELECT * FROM mybatis.user </select>
-
-
测试
-
// 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
- 地址:https://pagehelper.github.io/
八、使用注解开发
1. 接口的作用
- 解耦
- 可扩展
- 提高复用
2. 关于接口的理解
- 接口从更深层次的理解,应是定义(规范、约束)与实现(名实分离的原则)的分离
- 接口的本身反映了系统 设计人员对系统的抽象理解
- 接口应有两类:
- 第一类是对 一个个体的抽象,它可对应为一个抽象提(abstract class)
- 第二类是对一个个体某个方面的抽象,即形成一个抽象面(interface)
- 一 个体有可能有多个抽象面,抽象体与抽象面是有区别的
3. 三个面向的区别
- 面向对象:指的是我们考虑问题时,以对象为单位,考虑它的属性及方法
- 面向过程:指的是我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现
- 面向接口:指的是接口设计与非接口设计,是针对复用技术而言的,与面对对象(过程)不是一个问题,更多的体现就是对系统整体的架构
4. 内容:
-
注解在接口上实现
-
@Select("SELECT id,name,pwd as password FROM user") List<User> getUsers();
-
-
需要在核心配置文件中绑定接口
-
<!--绑定接口--> <mappers> <mapper class="com.zhout.dao.UserMapper"/> </mappers>
-
-
测试
5. 实现机制
- 本质:反射机制实现
- 底层:动态代理
6. Mybatis 详细的执行流程
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2ZBy1UIZ-1622949364087)(KS1_Step05_MyBatis详解.assets/1622949223756.png)]
7.使用注解来CRUD
-
我们可以在工具类创建的时候实现自动提交事务
-
// SqlSession 完全包含了面向数据库执行 SQL 命令 所需的所有方法 public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(true); }
-
-
编写接口,增加注解
-
// 方法存在多个参数,所有的参数前面都要加上 @Parm("id") 注解 @Select("SELECT id,name,pwd as password FROM user where id = #{id}") User getUserByID(@Param("id") int id);
-
-
【重点注意】:我们必须将接口注册绑定到我们的核心配置文件中
-
<!--绑定接口--> <mappers> <mapper class="com.zhout.dao.UserMapper"/> <!--没有使用接口的话,可以扫描--> <mapper resource="com/zhout/dao/*Mapper.xml"/> </mappers>
-
-
关于 @Param(“id”)注解:
-
基本数据类型的参数或者String类型,需要加上
-
引用类型的不需要添加
-
如果只有一个基本类型的话,可以忽略,但是建议 都加上
-
我们在SQL中引用的就是我们这里的@Param()中设定的属性名
-
#{}——能够很大程度上防止sql注入 ${}——无法防止sql注入 注意:Mybatis排序时,使用order by 动态参数时需要注意,需要使用${}而不是使用#{}
-
-
九、Lombok
1. 解释其是啥:
- java library
- plugins
- build tools
- with one annotation your class
2. 使用步骤
-
相关注解
-
@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
-
-
常用的Lombok注解
-
@Data:无参构造、get、set、toString、hashCode、equals @AllArgsConstructor @NoArgsConstructor @EqualsAndHashCode
-
十、多对一处理
1. 多对一
- 多个学生对应一个老师
- 对于学生这边而言,关联 :多个学生,关联一个老师【多对一】
- 对于老师而言,集合:一个老师,有多个学生【一对多】
2. SQL
-
表结构
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qUqMrU0J-1622949364089)(KS1_Step05_MyBatis详解.assets/1622871811154.png)]
-
按照查询嵌套处理:
-
<!--思路: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>
-
-
按照结果嵌套处理:
-
<!--思路二(按照结果嵌套处理):--> <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>
-
-
回顾Mysql 多对一查询方式:
- 子查询
- 链表查询
十一、一对多处理
-
一个老师拥有多个学生
- 对于老师而言,就是一对多的关系
-
按照结果嵌套处理
-
<!--按照结果嵌套处理--> <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>
-
-
按照查询嵌套处理
-
<!--按照查询嵌套处理--> <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. 小结
- 关联 - association 【多对一】
- 集合 - collection 【一对多】
- javaType & ofType
- javaType: 用来指定实体类中属性的类型
- ofType: 用来指定映射到 List 或者集合中的 pojo 类型,泛型中的约束类型
- 【注意点】:
- 保证SQL 的可读性,尽量保证通俗易懂
- 注意一对多和多对一中的属性名和字段的问题
- 如果问题不好排查错误,可以使用日志,建议使用Log4j
- 慢SQL:1s - 1000s
- MySQL引擎:InnoDB、MyISAM的区别
- InnoDB底层原理:
- 索引:为什么要使用索引、索引的结构、索引的分类、如何去操作
- 索引优化:
十二、动态 SQL
1、什么是动态SQL
-
动态SQL 就是指根据 不同的条件生成不同的SQL语句
-
if choose (when, otherwise) trim (where, set) foreach
-
2、搭建环境
-
创建数据库表:
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;
-
基础步骤
- 导包
- 编写配置文件
- 编写实体类
- 编写实体类对应 Mapper接口 和 Mapper.xml文件
- 编写测试类
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片段
-
有的时候,可能会将一些功能的部分抽取出来,方便复用 。
-
使用SQL 标签抽取公共的部分
-
在需要使用的地方使用 Include 标签引用即可
-
<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>
-
-
-
注意事项
- 最好基于单表来定义SQL片段
- 不要存在 where 标签
9、动态SQL-小结
动态SQL 就是在拼接SQL语句,我们只要保证SQL的正确性,按照SQL 的格式,去排列组合。
【建议】:
- 先在MySQL中写出完整的SQL语句,再对应地去修改成为我们的动态SQL,从而实现通用。
十三、缓存
1、简介
查询,连接数据库,耗资源
一次查询的结果,给他存在一个可以直接取到的地方——》内存:缓存
我们再次查询相同数据的时候,直接走缓存,就不用走数据库了
-
什么是缓存
- 存在内存中的临时数据
- 将 用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)内查询了。改为从缓存中查询,从而提高 查询效率,能解决高并发系统的性能问题。
-
为什么要使用缓存
- 减少和数据库的交互次数,减少系统开销,提高系统效率。
-
什么样的数据能使用缓存
- 经常查询并且不经常改变的数据
-
系统三高:高性能、高并发、高可用
-
数据库原则:原子性、事务性、隔离性
- 读写分离
- 主从复制:三写,一读
- 读写分离
2、Mybatis缓存
- Mybatis 包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
- Mybatis 系统中默认定义了两级缓存:一级缓存 和 二级缓存
- 默认情况下,只有一级缓存开启和配置。(SqlSession 级别的缓存,也称为本地缓存)
- 二级缓存需要手动开启和配置,他是基于namespace 级别的缓存
- 为了提高扩展性,Mybatis 定义了缓存接口Cache。我们可以通过实现Cache 接口来自定义二级缓存
3、一级缓存
- 一级缓存也叫本地缓存——SqlSession
- 与数据库同一次会话期间查询到的数据会放在本地缓存中
- 以后 如果需要获取相同的数据,直接从缓存中拿,没有必要再去查询数据库
- 一级缓存 - 缓存失效的情况:
- 查询不同的东西
- 增删改操作,可能会改变原来的数据 ,所以必定会刷新缓存
- 查询不同的Mapper.xml ——此种情况,二级缓存也会失效
- 手动清理缓存
- 小结:
- 一级缓存默认是开启的,只在一次 SqlSession 中有效,也就是”拿到连接“到”关闭连接“的这个区间
- 一级缓存就是一个map
4、二级缓存
-
二级缓存也叫全局缓存,一级缓存作用域太低,所以诞生了二级缓存
-
基于namespace 级别的缓存,一个名称空间,对应一个二级缓存
-
工作机制:
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中
- 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了 ,一级缓存中的数据被保存到二级缓存中
- 新的会话查询信息,就可以从二级缓存中获取内容
-
使用步骤:
-
开启全局缓存
-
<!--显示的开启全局缓存--> <setting name="cacheEnabled" value="true"/>
-
-
在要使用二级缓存的Mapper.xml中开启:
-
增加cache标签
-
<!--在当前Mapper.xml中使用二级缓存,设置:先进先出、60秒刷新、512个、只读--> <cache />
-
-
也可以 自定义参数
-
<!--在当前Mapper.xml中使用二级缓存,设置:先进先出、60秒刷新、512个、只读--> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
-
-
-
我们需要将实体类序列化 ,否则就会报错。
-
-
小结:
- 只要开启了二级缓存,在同一个Mapper.xml下就有效
- 所有的数据都会先放在一级缓存中
- 只有当会话提交,或者关闭的时候,才会提交到二级缓存中
5、Mybatis 缓存原理
- 先看二级缓存中有没有
- 再看一级缓存中有没有
- 查询数据库
6、自定义缓存——ehcache
简介:
- Ehcache 是一个纯Java 的进程内缓存框架,具有快捷、精干等特点,是Hiberrnate 中默认的CacheProvider。
- Ehcache 是一种广泛使用的开源Java 分布式缓存。
-
先导包
-
<!-- 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>
-
-
在mybaits-config.xml中启用全局缓存
-
<!--设置项目--> <settings> <!--显示的开启全局缓存--> <setting name="cacheEnabled" value="true"/> </settings>
-
-
在Mapper.xml中指定使用ehcache 缓存实现。
-
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
-
-
ehcache.xml
-
<?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>
-
-
后续需要使用Redis 数据库来做缓存,K-V键