MyBatis入门(从零到一)

MyBatis官方文档地址

https://mybatis.org/mybatis-3/zh/index.html

实际操作可能会遇见的问题

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

引入MyBatis依赖

创建Maven项目

引入MyBatis的依赖和Mysql驱动,还有junit必须要引入,引入lombok(为了不写get,set)
注意:mysql驱动的版本要和mysql的版本能匹配上

<dependencies>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.17</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.4</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.12</version>
    </dependency>
</dependencies>
<!--配置静态资源过滤,使得maven编译后能将静态资源部署到编译完的代码中-->
<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
    </resources>
</build>

定义pojo

@Data
public class User {
    private Integer id;
    private String name;
    private String password;
    private Date createTime;
    private Date modifyTime;
    private Boolean isDeleted;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                ", createTime=" + createTime +
                ", modifyTime=" + modifyTime +
                ", isDeleted=" + isDeleted +
                '}';
    }
}

配置mybatis-config.xml
如果数据库中的字段名是is_deleted,pojo的字段是isDeleted,那么需要开启驼峰命名转换

<?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>
    <settings>
        <!--开启驼峰命名映射-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    <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://localhost:3306/MybatisStudy?useSSL=true&amp;autoReconnect=true&amp;useUnicode=true&amp;characterEncoding=UTF8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <!--注册mapper-->
    <mappers>
        <mapper resource="com/jxw/dao/UserMapper.xml"/>
    </mappers>
</configuration>

定义dao接口,就一个简单的get方法

public interface UserMapper {
    List<User> getUser();
}

定义mapper
mapper中的namespace指定对应的接口
每一个select,update,delete,insert标签都对应一个接口中的方法
id对应的是方法名
resultType需要写全限定名,List<>返回值的不需要额外处理,会自动填充

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.jxw.dao.UserMapper">

    <select id="getUser" resultType="com.jxw.pojo.User">
		select * from user
	</select>
</mapper>

定义工具类,后面整合Spring之后就交给Spring管理了,但是现在还没有整合,我们需要使用他来测试

public class SqlSessionUtil {
    //写个单例
    private static SqlSessionFactory sqlSessionFactory;

    static {
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }
}

建表,然后再插入一条数据,要不然空表没法测啊

create table if not exists user(
    `id` int auto_increment primary key comment '主键',
    `name` nvarchar(45) not null comment '姓名',
    `password` varchar(45) not null comment '密码',
    `create_time` datetime not null comment '创建时间',
    `modify_time` datetime comment '修改时间',
    `is_deleted` bool not null comment '是否已删除'
)engine = InnoDB

INSERT INTO 
user (name, password, create_time, modify_time, is_deleted)
values ('jxw','123456',NOW(),null,false);

配置基本完成,下面是测试方法

@Test
public void TestGet(){
    //使用该种方式,jdk会自动帮我们关闭
	try(SqlSession sqlSession = SqlSessionUtil.getSqlSession()){
	    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
	    List<User> list = userMapper.getUser();
	    list.stream().forEach(e -> System.out.println(e));
	}
}

执行结果:
在这里插入图片描述
正确查询出来了,但是有一条红色的提示,内容为
Loading class com.mysql.jdbc.Driver'. This is deprecated. The new driver class iscom.mysql.cj.jdbc.Driver’. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.

大概意思就是com.mysql.jdbc.Driver被弃用了,请使用com.mysql.cj.jdbc.Driver
修改mybatis-config.xml
在这里插入图片描述
在这里插入图片描述
修改过后,红字提示成功的消失了

CURD各写一遍

接口增加方法

public interface UserMapper {
    List<User> getUser();
    User getUserById(Integer id);
    int  addUser(User user);
    int update(User user);
    int delete(Integer id);
}

xml配置上

<select id="getUser" resultType="com.jxw.pojo.User">
	select * from user
</select>

<select id="getUserById" resultType="com.jxw.pojo.User" parameterType="int">
    select * from user where id = #{id}
</select>
<insert id="addUser" parameterType="com.jxw.pojo.User">
    insert into user(name, password, create_time, modify_time, is_deleted) value (#{name}, #{password}, #{createTime}, #{modifyTime}, #{isDeleted})
</insert>

<update id="update" parameterType="com.jxw.pojo.User">
    update user set name=#{name},password=#{password},modify_time=#{modifyTime},is_deleted=#{isDeleted} where id=#{id}
</update>

<delete id="delete" parameterType="int">
    delete from user where id=#{id}
</delete>

当parameterType指定为某个对象时,在标签中可以直接使用该对象的属性
测试方法,增删改的操作一定要提交事务

@Test
public void TestGet(){
    //使用该种方式,jdk会自动帮我们关闭
    try(SqlSession sqlSession = SqlSessionUtil.getSqlSession()){
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> list = userMapper.getUser();
        list.stream().forEach(e -> System.out.println(e));
        User user =  userMapper.getUserById(1);
        System.out.println(user);
    }
}

@Test
public void testAdd(){
    //使用该种方式,jdk会自动帮我们关闭
    try(SqlSession sqlSession = SqlSessionUtil.getSqlSession()){
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User user1 = new User();
        user1.setName("jxw2");
        user1.setCreateTime(new Date());
        user1.setIsDeleted(false);
        user1.setPassword("654321");
        userMapper.addUser(user1);
        sqlSession.commit();
        List<User> list = userMapper.getUser();
        list.stream().forEach(e -> System.out.println(e));
    }
}
@Test
public void testUpdate(){
    //使用该种方式,jdk会自动帮我们关闭
    try(SqlSession sqlSession = SqlSessionUtil.getSqlSession()){
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User user1 = new User();
        user1.setId(3);
        user1.setName("jxw2");
        user1.setModifyTime(new Date());
        user1.setIsDeleted(true);
        user1.setPassword("654321");
        userMapper.update(user1);
        sqlSession.commit();
        List<User> list = userMapper.getUser();
        list.stream().forEach(e -> System.out.println(e));
    }
}
@Test
public void testDelete(){
    //使用该种方式,jdk会自动帮我们关闭
    try(SqlSession sqlSession = SqlSessionUtil.getSqlSession()){
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        userMapper.delete(4);
        sqlSession.commit();
        List<User> list = userMapper.getUser();
        list.stream().forEach(e -> System.out.println(e));
    }
}

都能跑通,执行结果就不贴了
需要注意的一点是,标签里面是什么什么语句就要用什么标签,不要用混淆了

使用map参数类型

UserMapper接口中新加一个方法

int update2(Map<String,Object> map);

xml中实现该接口,注意这里参数类型是【map】,sql中的参数名我一顿瞎改,只要在map中有对应的key,就能获取到

<update id="update2" parameterType="map">
    update user set name=#{name1},password=#{password1},modify_time=#{modifyTime1},is_deleted=#{isDeleted1} where id=#{userId}
</update>

测试方法

@Test
public void testUpdateByMap(){
    //使用该种方式,jdk会自动帮我们关闭
    try(SqlSession sqlSession = SqlSessionUtil.getSqlSession()){
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        Map<String,Object> user1 = new HashMap<>();
        user1.put("userId",3);
        user1.put("name1","jxwByMap");
        user1.put("modifyTime1",new Date());
        user1.put("isDeleted1",true);
        user1.put("password1","654321");
        userMapper.update2(user1);
        sqlSession.commit();
        List<User> list = userMapper.getUser();
        list.stream().forEach(e -> System.out.println(e));
    }
}

成功修改,可以得知,使用map类型传递参数时,直接在sql中写key就能获取到value,多个参数的时候可以使用map,或者注解,后面再写注解

resultMap标签的简单使用(结果集映射)

在mapper.xml文件中添加如下标签,表示查询的结果与pojo实体字段的映射

  • resultMap标签的属性
    • type
      表示结果集要映射到的对象,要写全限定名
    • id
      表示本标签在文件中的唯一标识
  • resultMap的子标签的属性
    • column指定列名,property指定pojo对象的属性名,表示指定列名映射到属性名

id标签表示该字段是该表的主键

<resultMap type="com.jxw.pojo.User" id="user">
    <id column="id" property="id"/>
    <result column="name" property="name"/>
    <result column="password" property="password"/>
    <result column="create_time" property="createTime"/>
    <result column="modify_time" property="modifyTime"/>
    <result column="is_deleted" property="isDeleted"/>
</resultMap>

配置解析

核心配置文件

官方建议名称
mybatis-config.xml
MyBatis的配置文件包含了影响MyBatis行为和属性的信息

configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
    environment(环境变量)
        transactionManager(事务管理器)
        dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)

环境变量

在这里插入图片描述
environments中可以配置多套environment,default属性指向environment的id,并且表示当创建SqlSessionFactory不指定环境时默认使用的环境
尽管MyBatis可以配置多套环境,但是一个SqlSessionFactory的实例只能使用一个环境。
transactionManager,事物管理器有两种,JDBC和MANAGED
JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。例如:

<transactionManager type="MANAGED">
  <property name="closeConnection" value="false"/>
</transactionManager>

如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。

配置(properties)

这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。
定义【db.properties】文件,创建在resources目录下

driver = com.mysql.cj.jdbc.Driver
url = jdbc:mysql://localhost:3306/MybatisStudy?useSSL=true&amp;autoReconnect=true&amp;useUnicode=true&amp;characterEncoding=UTF8
username = root
password = 123456

在mybatis-config.xml中添加properties标签
properties 标签中可以配置propertie子标签来添加额外的属性,但是当属性名称和外部文件冲突时,会优先使用外部文件的属性值

  <properties resource="db.properties"/>

然后mybatis-config.xml中的环境配置可以修改成如下,使用 ${driver} 即可获取到配置文件中的driver属性,这里需要注意的一点是,在xml中需要将 & 符号转义,而properties中则不需要,直接使用 & 就可以

<environments default="development">
	 <environment id="development">
        <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>

看起来清爽多了

类型别名(typeAliases)

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。

  • 单独为每一个类起别名
<typeAliases>
  <typeAlias alias="user" type="com.jxw.pojo.User"/>
</typeAliases>
  • 也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean
<typeAliases>
  <package name="com.jxw.pojo"/>
</typeAliases>

包扫描在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。
官方建议用首字母小写,我们首字母大写也可以。
下面是注解的用法, 使用@Alias指定别名,这种方式比包扫描的自动别名优先级要高

@Alias("user")
public class User {}

在起完别名后,我们在写mapper的时候,resultType和parameterType再也不用写全限定名了

映射器(mappers)

1)使用resource方式来指定xml文件
我们之前使用了resource属性指定xml的方式,由于我们在UserMapper.xml指定了namespace,所以这种注册方式除了xml的路径要写对之外没有需要注意的地方

<!--注册mapper-->
<mappers>
     <mapper resource="com/jxw/dao/UserMapper.xml"/>
</mappers>

2)使用class方式来指定接口,这里需要注意,接口必须和xml在同一个包下,并且名称相同,才能正确的进行映射

<mappers>
     <mapper class="com.jxw.dao.UserMapper"/>
</mappers>

3)包扫描,使用package指定包名,扫描该包下的全部文件,这个方式和class方式需要注意的是一样的,接口必须和xml在同一个包下,并且名称相同

<mappers>
    <package name="com.jxw.dao"/>
</mappers>

生命周期和作用域

  • SqlSessionFactoryBuilder
    • 作用域是方法作用域(也就是局部方法变量)
    • 该对象用来创建SqlSessionFactory,该实例会持有xml配置文件的解析资源,所以尽量将该实例的生命周期控制在一个方法内,以保证资源能尽快释放
  • SqlSessionFactory
    • 该对象的实例为应用作用域
    • 可以理解为数据库连接池,没有理由去关闭它
    • 最好使用单例或者静态单例来保存该对象的实例
  • SqlSession
    • 可以理解为连接到连接池的一个请求
    • 它的最佳的作用域是请求或方法作用域,绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。
    • 因为该对象的实例不是线程安全的,因此是不能共享的,同时要确保该实例在每次请求完成后被正确的关闭,否则资源会被占用。

日志的配置

在mybatis-config.xml文件中添加以下节点,mybatis默认不开启日志

<settings>
    <!--日志-->
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

官方文档中描述有以下几种日志可用,其中STDOUT_LOGGING为mybatis内置的默认实现

  • SLF4J
  • LOG4J
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING
  • NO_LOGGING

推荐使用【LOG4J】
因为LOG4J可以另外使用单独的配置文件来配置日志的各种设置,有关log4j的配置搜一下吧

分页实现

使用limit

定义接口

List<User> getUserByPage(Map map);

编写mapper.xml文件

<select id="getUser" resultType="com.jxw.pojo.User" parameterType="map">
	select * from user limit #{pageSize} offset #{startIndex}
</select>

使用map传参,给map中传递参数pageSize和startIndex即可

使用pageHelper

整合Spring的笔记中再写吧

注解开发

在接口上面使用@Select,@Delete,@Update,@Insert注解,注解的参数为sql语句
使用@Param注解标识参数名称,可以为参数指定一个名称,比如下面的代码就是一个查询语句,并且为接口的id参数指定名称为aaa,然后在sql中使用aaa获取参数值

@Select("select * from user where id = #{aaa}")
User getUserById(@Param("aaa") Integer id);

Mybatis执行流程

在这里插入图片描述

多对一

概念就不介绍了
在resultMap标签中使用 association 子标签
使用官网的例子,博客对象中有一个字段是叫author,该字段的类型是Author,下面演示了如何给author的各个字段来赋值

<resultMap id="detailedBlogResultMap" type="Blog">
  <result property="title" column="blog_title"/>
  <association property="author" javaType="Author">
    <id property="id" column="author_id"/>
    <result property="username" column="author_username"/>
    <result property="password" column="author_password"/>
    <result property="email" column="author_email"/>
    <result property="bio" column="author_bio"/>
    <result property="favouriteSection" column="author_favourite_section"/>
  </association>
</resultMap>

association表示该字段是个复杂对象

  • property表示对应的是哪个字段
  • javaType表示该字段的java类型

association里面的子标签和resultMap基本一样,所以我的理解是association类似resultMap,使用association相当于resultMap里面嵌套了一个resultMap

一对多

还是用官网的例子,一篇博客下有很多评论,所以博客实体下有个集合存储评论信息
该字段名为posts,类型为Post

<resultMap id="detailedBlogResultMap" type="Blog">
  <collection property="posts" ofType="Post">
    <id property="id" column="post_id"/>
    <result property="subject" column="post_subject"/>
    <association property="author" javaType="Author"/>
    <collection property="comments" ofType="Comment">
      <id property="id" column="comment_id"/>
    </collection>
    <collection property="tags" ofType="Tag" >
      <id property="id" column="tag_id"/>
    </collection>
    <discriminator javaType="int" column="draft">
      <case value="1" resultType="DraftPost"/>
    </discriminator>
  </collection>
</resultMap>

使用collection标签表示该字段是一个集合

  • ofType表示集合内对象的类型,association 使用的是javaType,这里是ofType,要注意
  • 其他部分基本一样,就还是类似嵌套的resultMap

多对一和一对多还有一种类似子查询的方式,collection或者association的select属性指定连接的sql语句,在执行某些查询用到该resultMap的时候便会执行子查询将该部分的数据填充进去,但是不建议使用这种方式,所以就不做记录了

动态SQL

if标签(配合where标签使用)

  • if标签可以判断传入的参数的值是否等于某个值,如果结果为true,则if标签内的语句片段会拼接到整个sql中,否则不拼接

  • if标签的test属性的值就是我们要写的判断表达式,表达式内可以直接使用参数的名称或参数的属性值

  • if标签内也可以使用include标签引用代码片段

  • where标签会自动帮助我们去除where后面的第一个and,所以当第一个条件不成立第二个条件成立的时候,也能成功执行,where帮助我们去掉了紧挨着它的and

示例代码:

<select id="getUserByParams" resultType="com.jxw.pojo.User" parameterType="com.jxw.pojo.User">
	select * from user
	<where>
	   <if test="name != null">
	       name = #{name}
	   </if>
	   <if test="isDeleted != null">
	       and is_deleted = #{isDeleted}
	   </if>
	</where>
</select>

choose when otherwise

choose when otherwise时一组标签,此标签很多人说是类似java中的switch case,但是在我看来,这明显和sql中的case when更像

  • choose标签表示一个选择器
  • when时choose的子标签,它和if的用法一样,传入一个判断条件,满足条件时走入
  • otherwise标签写在最底下,当上面的when都不满足时才走入这个标签

这一组标签的逻辑:从上往下走,当遇到第一个when满足的条件后不再继续往下走,所有when都不满足走otherwise标签
otherwise标签可以不写,当所有when都不满足并且otherwise标签不存在的时候就哪个分支都不会进入

<where>
    <choose>
        <when test="name != null">
            name = #{name}
        </when>
        <when test="isDeleted != null">
            and is_deleted = #{isDeleted}
        </when>
    </choose>
</where>

where set trim

  • where标签在if标签中写了
    • where标签会自动帮助我们去除where后面的第一个and,所以当第一个条件不成立第二个条件成立的时候,也能成功执行,where帮助我们去掉了紧挨着它的and
  • set标签就对应sql中的set,该标签会帮助我们去除最后一个逗号
  • trim标签其实相当于where和set的底层,他给了我们很多自定义的设置,where标签和set标签可以看成是对trim标签的封装

和where一样功能的trim

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

和set一样功能的trim

<trim prefix="SET" suffixOverrides=",">
</trim>
  • prefix属性指定该段sql的前缀
  • prefixOverrides指定子片段的前缀,如果该属性的值紧挨着前缀会被移除掉
  • suffixOverrides指定子片段的后缀,如果整个片段的最后是这个值则会移除掉
  • 子片段的前缀和后缀可以指定多个值,多个值使用 | 来进行分割

foreach

用来遍历集合,在where in 和批量插入应该用的比较多,先看一下官网的示例

<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  WHERE ID in
  <foreach item="item" index="index" collection="list"
      open="(" separator="," close=")">
        #{item}
  </foreach>
</select>
  • collection表示使用的集合
  • index 可以获取到当前迭代的下标
  • item 表示集合中的单个对象,即当前迭代到的对象,在循环里面可以使用该属性的值来获取对象信息
  • open 表示循环开始之前,在最前面拼接的sql
  • close 表示循环结束,在最后面拼接的sql
  • separator 表示在两个迭代的sql中间拼接的分割符

sql片段

该示例演示了创建sql片段和引用该片段
使用sql标签创建一个片段,使用include引用一个片段

<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>

<select id="selectUsers" resultType="map">
  select
    <include refid="userColumns"><property name="alias" value="t1"/></include>,
    <include refid="userColumns"><property name="alias" value="t2"/></include>
  from some_table t1
    cross join some_table t2
</select>

include 标签的refid属性值填入sql标签的id,以此来进行引用
sql片段中使用${alias}占位符的时候,在include标签引用的时候可以使用property为其进行赋值

  • property标签
    • name指定要给引用的标签的哪个占位符赋值
    • value可以写指定的常量值,value也可以使用#{name}或者${name}这种占位符从传入的参数获取值,当value使用#{name}时,mybatis会默认使用参数化查询,${name}这种则会将该字段的值拼接到sql中

缓存

一级缓存

一级缓存失效的情况:

  • 查询不同的数据
  • 增删改操作会清理调原本的缓存
  • 使用了不同的Mapper.xml
  • 调用sqlSession.clearCache()手动清理缓存

二级缓存

  • 二级缓存也交全局缓存,一级缓存作用域太低了,所以产生了二级缓存

  • 基于namespace级别的缓存,一个命名空间对应一个二级缓存

  • 工作机制

    • 当前会话查询一条数据,这条数据就会被缓存到当前会话的一级缓存中
    • 如果当前会话关闭了,这个会话对应的一级缓存就饿没有了,但是我们希望会话关闭后一级缓存的数据可以缓存到二级缓存中
    • 新的会话查询数据,就可以从而及缓存中获取内容
    • 不同的mapper查出的数据会放在自己对应的缓存(map)中
  • 步骤

    • 开启全局缓存,mybatis-config.xml
    <settings>
       <!--显式的开启全局缓存,即使默认就是true-->
       <setting name="cacheEnabled" value="true"/>
    </settings>
    
    • 开启二级缓存,在mapper.xml文件中加入,表示当前mapper加入二级缓存
    <cahce/>
    

    当使用这种默认的二级缓存策略的时候,pojo类需要实现Serializable接口,否则会报错
    当cache中属性设置,readOnly = true的时候,则不需要实现Serializable也能正常运行
    也可以对单个select标签进行设置是否使用缓存,刷新缓存等操作,使用方式是给select标签的useCache等属性赋值

  • 小结

    • 只要开启了二级缓存,在同一个mapper下就有效
    • 所有的数据都会先放进一级缓存中
    • 只有当会话提交和会话关闭的时候才会提交到二级缓存

缓存读取顺序

1.先查二级缓存
2.再查一级缓存
3.查询数据库
从上至下查询,直到查询到数据或者走到最底层才会返回结果
查询完成后,最底层的结果会先缓存到一级缓存,然后当会话关闭或者提交的时候,缓存到二级缓存

自定义缓存Ehcache

就不多介绍了
在pom文件中引入mybatis整合ehcache的依赖

<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.2.0</version>
</dependency>

在mapper.xml中指定缓存的实现方式

<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">
<!--    //默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。-->
    <defaultCache />
<!--    //对象是否永久有效,一但设置了,timeout将不起作用。-->
<!--    eternal="false"-->
<!--    //内存缓存最大数目-->
<!--    maxElementsInMemory="1000"-->
<!--    //硬盘最大缓存个数-->
<!--    maxElementsOnDisk="5000"-->
<!--    //是否保存到磁盘,当系统宕机时-->
<!--    overflowToDisk="false"-->
<!--    //是否缓存虚拟机重启期数据-->
<!--    diskPersistent="false"-->
<!--    //设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。-->
<!--    timeToIdleSeconds="0"-->
<!--    //设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。-->
<!--    timeToLiveSeconds="600"-->
<!--    //当达到缓存最大值限制时,缓存清除策略-->
<!--    memoryStoreEvictionPolicy="LRU" />-->

    <!-- 这里以example为例 -->
    <cache name="example"/>
<!--    //缓存名称-->
<!--    name="example"-->
<!--    eternal="false"-->
<!--    maxElementsInMemory="100"-->
<!--    overflowToDisk="false"-->
<!--    diskPersistent="false"-->
<!--    timeToIdleSeconds="0"-->
<!--    timeToLiveSeconds="300"-->
<!--    memoryStoreEvictionPolicy="LRU" />-->
</ehcache>

<!--        diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。-->
<!--        diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。-->
<!--        diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。-->
<!--        clearOnFlush:内存数量最大时是否清除。-->
<!--        memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。-->

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值