前言
- Mybatis 是什么
- 我们为什么要学习 Mybatis
- 持久化是什么
- 持久层是什么
- mybatis 依赖
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
一、Mybatis 程序
1.1、思路:搭建环境 -> 导入 mybatis 的 jar 包 -> 编写代码 -> 测试
- 1相关jar包
<dependencies>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.26</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.6</version>
</dependency>
<!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
<!--在build中配置resources,防止资源导出失败的问题-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
1.2、mybatis 程序
- 写工具类读取 mybatis 的配置文件获取 sqlsession 对象。
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class MybatisUtil {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
// 使用mybatis第一步,获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
// 有了SqlSessionFactory,我们就可以获得sqlsession的实例了
// sqlsession对象完全包含了面向数据库执行sql命令需要的所有方法
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession();
}
}
- 配置 mybatis 配置文件,连接数据库。
// mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://101.37.147.181:3306/z_mybatis?useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="xxx"/>
</dataSource>
</environment>
</environments>
<!--每一个mapper.xml都需要在mybatis核心配置文件中注册-->
<mappers>
<mapper resource="com/sywl/mapper/UserMapper.xml"></mapper>
</mappers>
</configuration>
- 搭建环境,编写代码。(User,UserMapper 接口, UserMapper.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.sywl.mapper.UserMapper">
<delete id="deleteUser">
delete from user where id = #{userId}
</delete>
<select id="selectUserList" resultType="com.sywl.entity.User">
select * from user
</select>
<select id="selectById" resultType="com.sywl.entity.User">
select * from user where id = #{id}
</select>
<insert id="insertUser" parameterType="com.sywl.entity.User">
insert into user(name,age) values (#{name},#{age})
</insert>
</mapper>
- 测试。
@Test
public void test01(){
// 1.获取sqlsession对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
// 2.方式一:获取UserMapper接口,去执行方法
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.selectUserList();
for (User user : users) {
System.out.println(user);
}
}
@Test
public void test05(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
List<User> userList = sqlSession.selectList("com.sywl.mapper.UserMapper.selectUserList");
for (User user : userList) {
System.out.println(user);
}
}
1.3、可能出现的问题
- 配置文件没有注册。(mybatis 配置文件中的没有配置)
- 绑定接口错误。(UserMapper.xml 文件中的 namespace 配置不对)
- 方法名不对。(UserMapper.xml 文件中 id 对应的方法名不对)
- 返回类型不对。(UserMapper.xml 文件的 resultType 类型不对)
- 资源导出失败问题。(需要在 pom.xml 中的 build 中配置 resources)
1.4、注意点
- resource 绑定 mapper,需要使用路径 /
二、crud
2.1、万能 Map
- Map 传递参数,直接在 SQL 中取出 Map 的 key。
Map map = new Map();
map.set("name","zhangsan");
map.set("userId",1);
mapper.getUserById(map);
// UserMapper
User getUserById(Map map);
// UserMapper.xml
<select id="getUserById" parameterType="map">
select * from user where username=#{name} or user_id=#{userId}
</select>
- 对象传递参数,直接在 SQL 中取对象的属性。`
User user = new User();
user.setUsername("zhangsan");
user.setUserId(1);
mapper.getUserById(user);
// UserMapper
User getUserById(User user);
// UserMapper.xml
<select id="getUserById" parameterType="user">
select * from user where username=#{username} or user_id=#{userId}
</select>
- 只有一个基本类型参数的情况下,可以直接在 SQL 中取到,省略 parameterType。
mapper.getUserById(id);
// UserMapper
User getUserById(int id);
// UserMapper.xml
<select id="getUserById">
select * from user where username=#{username} or user_id=#{userId}
</select>
- 多个参数传递时,用 Map,或者 Mapper 层使用@Param注解。
2.2、模糊查询
- java 代码执行时传递通配符。
List<User> userList = mapper.getUserList("%李%");
- 在 sql 拼接时使用通配符。
select * from user where username like "%"#{username}"%"
三、配置优化
3.1、总配置
//db.properties 加上jdbc前缀避免出错(连接不到数据库)
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8
jdbc.username=root
jdbc.password=xxx
// mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--引入外部配置文件:可以增加属性-->
<!--如果外部配置文件和增加的属性都用,优先使用外部配置-->
<properties resource="db.properties"><!--db.properties在resources目录下可以直接写文件名-->
<property name="username" value="root"/>
<property name="password" value="xxx"/>
</properties>
<!--settings配置-->
<settings>
<!--全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。默认true-->
<setting name="cacheEnabled" value="true"/>
<!--延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。
特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。默认false-->
<setting name="lazyLoadingEnabled" value="默认false"/>
<!--允许JDBC支持自动生成主键,需要数据库驱动支持。默认false-->
<setting name="useGeneratedKeys" value="false"/>
<!--指定MyBatis所用日志的具体实现,未指定时将自动查找。默认未指定-->
<setting name="logImpl" value="LOG4J"/>
</settings>
<!--别名设置:1给实体类起别名,2配置一个包名-->
<typeAliases>
<!--<typeAlias type="com.sywl.entity.User" alias="User"></typeAlias>-->
<package name="com.sywl.entity"/>
</typeAliases>
<environments default="development">
<!--环境一-->
<environment id="development">
<!--mybatis事务管理器:默认时JDBC,还有一个managed-->
<transactionManager type="JDBC"/>
<!--mybatis数据源:默认POOLED,还有UNPOOLED和JNDI-->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
<!--环境二-->
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!--mappers映射器-->
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
3.2、相关配置属性
- properties(属性)
- settings(设置)
- typeAliases(类型别名)
- environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
- environment(环境变量)
- mappers(映射器)
- 方式一:resource(推荐使用)
<mappers>
<!--每一个Mapper.xml文件都需要在核心配置文件中注册-->
<mapper resource="com/sywl/mapper/UserMapper.xml"/>
</mappers>
- 方式二:使用 class 文件绑定注册
<mappers>
<!--每一个接口都需要在核心配置文件中注册-->
<mapper class="com.sywl.mapper.UserMapper"/>
</mappers>
// 注意:
//1接口和它的Mapper.xml配置文件必须同名。
//2接口和它的Mapper.xml配置文件必须在同一个包下。
- 方式三:使用扫描包进行绑定注册。
<mappers>
<package name="com.sywl.mapper"/>
</mappers>
// 注意:
//1接口和它的Mapper.xml配置文件必须同名。
//2接口和它的Mapper.xml配置文件必须在同一个包下。
四、生命周期和作用域
生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m88ySwVD-1659852230034)(https://www.yuque.com/api/filetransfer/images?url=https%3A%2F%2Fkuangstudy.oss-cn-beijing.aliyuncs.com%2Fbbs%2F2021%2F04%2F10%2Fkuangstudy931d32bf-2f53-407c-933c-a7c230e517ef.jpg&sign=3586686b08a7089edd3390e676496f7e3d302253a457af337087b711c6a2ac0c)]
- SqlSessionFactoryBuider 作用域。
- 一旦创建了 SqlSessionFactory,就不再需要它了
- 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(局部方法变量)
- SqlSessionFactory 作用域。
- SqlSessionFactory 可以想象成连接池。
- SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
- 因此 SqlSessionFactory 的最佳作用域是应用作用域。有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。
- SqlSession 作用域。
- 可以视为连接到连接池的一个请求。
- SqlSession 的实例不是线程安全的,因此是不能被共享的。
- 所以它的最佳的作用域是请求或方法作用域。
- 用完之后关闭,否则资源会被占用。
- 为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。
五、ResultMap
5.1、ResultMap 结果集映射。
- 起别名
<mapper namespace="com.sywl.mapper.UserMapper">
<select id="getUserById" resultType="com.sywl.entity.User">
select id,username,pwd as password from User where id = #{id}
</select>
</mapper>
- ResultMap 结果集映射(可以只映射字段名和属性名不同的)
<resultMap id="userMap" type="com.sywl.entity.User">
<id column="id" property="id"/><!--数据库字段id和实体类属性id名称一致可省略id的映射-->
<result column="username" property="username"/><!--数据库字段username和实体类属性username名称一致可省略username的映射-->
<result column="pwd" property="password"/><!--数据库字段pwd和实体类属性password名称不一致不可省略的映射-->
</resultMap>
<select id="getUserById" resultMap="userMap">
select id,username,pwd from User where id = #{id}
</select>
5.2、ResultMap 多对一映射
5.2.1、按照查询嵌套处理
@Data
public class Student {
private int id;
private String username;
private Teacher teacher;
}
<mapper namespace="com.sywl.mapper.StudentMapper">
<!--
思路:1.查询所有的学生
2.根据查询出来的学生表的tid字段,查找对应的老师
-->
<resultMap id="studentTeacherMap" type="com.sywl.entity.Student">
<id property="id" column="id"></id>
<result property="username" column="username"/>
<!--对象:association,tid是学生表中关联的老师的id-->
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>
<select id="getStudentList" resultMap="studentTeacherMap">
select * from student
</select>
<select id="getTeacher" resultType="Teacher">
select * from teacher where id = #{tid}<!--这个参数名推荐和上面column="tid"保持一致,但是不保持一致也不会出错-->
</select>
</mapper>
5.2.1、按照结果嵌套处理
@Data
public class Student {
private int id;
private String username;
private Teacher teacher;
}
<mapper namespace="com.sywl.mapper.StudentMapper">
<resultMap id="studentTeacherMap" type="com.sywl.entity.Student">
<id property="id" column="id"></id>
<result property="username" column="username"/>
<!--对象:association-->
<association property="teacher" javaType="Teacher">
<id property="id" column="tid"></id>
<result property="username" column="tname"></result>
</association>
</resultMap>
<select id="getStudentList" resultMap="studentTeacherMap">
select s.id,s.username,t.id tid,t.username tname from student s,teacher t where s.tid=t.id
</select>
</mapper>
5.3、ResultMap 一对多映射
5.3.1、按照查询嵌套处理
<mapper namespace="com.sywl.mapper.TeacherMapper">
<!--
思路:1.查询指定的老师
2.根据查询出来的老师的id字段别名是ttid,查找老师下对应的所有学生
-->
<resultMap id="teacherStudentMap" type="Teacher">
<id property="id" column="ttid"></id>
<!--ttid是老师表的id-->
<collection property="studentList" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId" column="ttid"/>
</resultMap>
<!--按照查询嵌套处理-->
<select id="getTeacherById" resultMap="teacherStudentMap">
select t.id ttid,t.* from teacher t where id=#{tid}
</select>
<select id="getStudentByTeacherId" resultType="Student">
select * from student where tid = #{ttid}
</select>
</mapper>
5.3.2、按照结果嵌套处理
@Data
public class Teacher {
private int id;
private String username;
private List<Student> studentList;
}
<mapper namespace="com.sywl.mapper.TeacherMapper">
<resultMap id="teacherStudentMap" type="Teacher">
<id property="id" column="tid"/>
<result property="username" column="tname"/>
<!--
复杂的属性单独处理,集合:collection
javaType=""指定实体类属性的类型;
集合中的泛型信息,使用ofType获取。
-->
<collection property="studentList" ofType="Student">
<id property="id" column="tid"/>
<result property="username" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
<!--按结果嵌套查询-->
<select id="getTeacherById" resultMap="teacherStudentMap">
select s.id sid,s.username sname,t.id tid,t.username tname from student s,teacher t where s.tid=t.id and t.id=#{tid}
</select>
</mapper>
六、日志工厂
6.1、mybatis 的日志配置
使用哪个日志实现在 mybatis-config.xml 设置中配置
- SLF4J
- LOG4J(掌握)
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING(掌握:标准日志输出)
- NO_LOGGING0
6.2、log4j
- Log4j 是 Apache 的一个开源项目,通过使用 Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI 组件。
- 我们也可以控制每一条日志的输出格式。
- 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
- 最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
6.3、mybatis 使用 log4j 步骤
- 导入 log4j 的 jar 包。
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
- 设置 mybatis 配置文件的日志实现类是 LOG4J
<settings>
<!--指定MyBatis所用日志的具体实现,未指定时将自动查找。默认未指定-->
<setting name="logImpl" value="LOG4J"/>
</settings>
- 书写 log4j.properties 配置文件。
# 将等级为DEBUG的日志信息输出到console和file两个目的地
log4j.rootLogger=DEBUG,console,file
# 控制台输出相关配置
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.Threshold=DEBUG
log4j.appender.console.Target=System.out
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%-5p][%d{yyyy-MM-dd HH\:mm\:ss,SSS}][%c] \:%m%n
# 文件输出的相关配置
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./logs/all.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%-5p][%d{yyyy-MM-dd HH\:mm\:ss,SSS}][%c] \:%m%n
# 日志输出级别
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
七、分页和注解开发
7.1、limit 分页
// i : 为查询结果的索引值(默认从0开始)
// n : 为查询结果返回的数量
select * from user limit i,n;
// 传一个参数时,意思是从第一个数据开始查询n个数据。
select * from user limit n;
一般传到后台服务中的数据是:currentPage(当前页数,一般从1开始)和 pageSize(记录行数即查询返回的数量);在实际应用到sql中的时候,必须经过换算页码来进行分页查询:
select * from user limit (currentPage-1)*pageSize,pageSize
7.2、RowBounds 分页
// 代码层设置分页,即sqlSession读取方法名执行方法时把设置好的RowBounds分页对象作为参数传入方法。
@Test
public void test02(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
RowBounds rowBounds = new RowBounds(1, 2);
List<User> userList = sqlSession.selectList("com.sywl.mapper.UserMapper.selectList", rowBounds);
}
// i是从哪一行开始查询,0代表从第一行;n是记录条数即每页显示的条数。(和limit的参数含义一致,在sql中换算页面也是一样)
RowBounds rowBounds = new RowBounds(i, n);
7.3、mybatis 的 PageHelper 分页插件。
详细使用文档:https://pagehelper.github.io/docs/howtouse/
- 导入PageHelper的jar包
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
- 在mybatis-config.xml中配置
<!--
plugins在配置文件中的位置必须符合要求,否则会报错,顺序如下:
properties?, settings?,
typeAliases?, typeHandlers?,
objectFactory?,objectWrapperFactory?,
plugins?,
environments?, databaseIdProvider?, mappers?
-->
<plugins>
<!-- com.github.pagehelper为PageHelper类所在包名 -->
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- 使用下面的方式配置参数,后面会有所有的参数介绍 -->
<property name="helperDialect" value="mysql"/>
</plugin>
</plugins>
- 在查询方法上使用
PageHelper.startPage(pageNo,pageSize);// 在这一行下面必须是查询方法;pageNo即currentPage,pageSize即每页显示条数。
List<User> userList = sqlSession.selectList("selectUserList");
7.4、注解开发
https://mybatis.org/mybatis-3/sqlmap-xml.html#Parameters
@Select("select * from user")
List<User> selectUser();
@Insert("insert into user(column1,column2) values(#{value1},#{value2})")
void insertUser(User user);
@Update("update user set column1=#{value1},column2=#{value2}")
void updateUser(User user);
@Delete("delete from user where userid=#{id}")
void deleteUserById(@Param("userid") int id);
八、mybatis 执行流程
九、动态 SQL
- 动态 SQL 是指根据不同的条件生成不同的 SQL 语句。
- 动态 SQL 本质还是 SQL 语句,只是我们可以在 SQL 层面,执行一个逻辑代码。
- 动态 SQL 就是在拼接 SQL 语句,我们只要保证 SQL 正确性,按照 SQL 格式去排列组合就可以了。
9.1、if
<select id="queryBlogIF" paremterType="map" resultType="blog">
select * from blog where 1=1
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</select>
9.2、choose,when,otherwise
<select id="queryBlogChoose" paremterType="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>
9.3、trim(where,set)
标签作用:
- where 标签元素中有子元素返回任何内容的情况下才插入 “where” 子句。
- 如果子句的开头为”AND” 或”OR”,where 标签元素会将他们去除。
标签作用:
- set 标签元素会动态的在行首插入 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>
9.4、foreach
<select id="selectPostIn" resultType="User">
select *
from user u
where id in
<foreach item="id" index="index" collection="ids"
open="(" separator="," close=")">
#{id}
</foreach>
</select>
9.5、SQL 片段
- 可以将一些功能的部分抽取出来,方便复用。
9.5.1、使用 SQL 标签抽取公共的部分。
<sql id="if-title-author">
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>
9.5.2、在需要的地方使用 include 标签引用即可。
<select id="queryBlogIF" paremterType="map" resultType="blog">
select * from blog
<where>
<include refid="if-title-anthor"></include>
</where>
</select>
十、缓存
- 什么是缓存[Cache]?
- 存在内存中的临时数据。
- 将用户经常查询的数据放在缓存中, 用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询, 从缓存中査询, 从而提高査询效率, 解决了高并发系统的性能问题。
- 为什么使用缓存?
- 减少和数据库的交互次数, 减少系统开销, 提高系统效率。
- 什么样的数据能使用缓存?
- 经常査询并且不经常改变的数据。
10.1、mybatis 缓存
- mybatis 默认定义了两级缓存:一级缓存和二级缓存
- 默认情况下,只有一级缓存开启。(SqlSession 级别的缓存)
- 二级缓存需要手动开启和配置,基于 namespace 级别的缓存。
- 为了提高扩展性,mybatis 定义了缓存接口 Cache。(我们可以通过实现 Cache 接口来自定义二级缓存)
10.2、一级缓存
10.2.1、测试步骤
- 开启日志。
- 测试在一个 SqlSession 中查询两次相同的记录。
- 查看日志输出。(只查询了一次数据库 SQL,第二次直接从缓存中数据)
10.2.2、一级缓存失效的情况
- 查询不同的东西。
- 增删改操作,可能会改变原来的数据,所以必定会刷新缓存。
- 查询不同的 mapper.xml
- 手动清理缓存。(sqlSession.clearCache();)
10.2.3、一级缓存总结
- 一级缓存默认是开启的。
- 一级缓存只在一次 SqlSession 中有效,即拿到连接到连接关闭的这个区间。
10.3、二级缓存
10.3.1、二级缓存使用步骤
- 开启全局缓存。
<settings>
<!--默认是true,建议显式的在配置文件中声明-->
<setting name="cacheEnabled" value="true"/>
</settings>
- 在要使用二级缓存的 mapper.xml 中开启。
<!--直接开启使用-->
<cache/>
<!--也可以自定义参数开启使用-->
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
LRU – 最近最少使用:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
- 测试。
- 需要把实体类序列化(实体类实现 Serializable 接口),否则可能会报错。
10.3.2、二级缓存总结
- 只要开启了二级缓存,在同一个 mapper 下有效。
- 所有的数据都会先放在一级缓存中。
- 只有当会话提交或关闭的时候,才会提交到二级缓存中。
- 可以在 mapper.xml 中的查询语句定义是否使用缓存
<select id="getUser"resultType="user" userCache="false">
select * from user
</select>
10.4、缓存原理
10.5、自定义缓存 - ehcache
Ehcache 是一种广泛使用的开源 Java 分布式缓存。主要面向通用缓存
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
配置一个 ehcache.xml 配置
总结
- mybatis 是一款优秀的持久层框架,可以把数据持久化;它支持定制化 sql,存储过程,结果集映射等。
- 使用的人多,传统的 jdbc 代码太复杂了,mybatis 框架简化了流程,自动化。
- 持久化是数据从瞬时状态转化到持久状态。内存的特性是断电即失,持久化指存到数据库中,io 文件持久化等。
- 持久层是完成持久化工作的代码块。