Mybatis笔记

感谢狂神,讲解清晰,以下是原视频【狂神说Java】Mybatis最新完整教程IDEA版通俗易懂_哔哩哔哩_bilibili的笔记

环境:

  • JDK 1.8
  • Mysql 8.0+
  • maven 3.6.1
  • IDEA

回顾:

  • JDBC
  • Mysql
  • Java基础
  • Maven
  • Junit

SSM框架:配置文件的。(看官网文档);

简介

什么是Mybatis

  • MyBatis 是一款优秀的持久层框架
  • 它支持自定义 SQL、存储过程以及高级映射。
  • MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
  • MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
  • MyBatis本是apache的一个iBatis,2010年这个项目由apache software foundation迁移到了google code,并且改名为MyBatis。2013年11月迁移到Github。

如何获得:

  • Maven仓库


    org.mybatis
    mybatis
    3.5.6

  • Github:https://github.com/mybatis
  • 中文文档:MyBatis中文网

持久化

数据持久化

  • 持久化就是将程序的数据在瞬时状态和持久状态转化的过程
  • 内存:断电即失
  • 数据库(jdbc),io文件持久化
为什么需要持久化?
  • 有的对象不能失去
  • 内存贵

持久层

Dao层、Sevice层、Controller层...

  • 完成持久化工作的代码块
  • 层界限十分明显

为什么需要Mybatis?

  • 把数据存到数据库中
  • 方便
  • 传统的JDBC代码太复杂。简化、框架、自动化
  • 不用Mybatis也可以。

优点:

  • 解除SQL和程序代码的耦合
  • 提供映射标签,支持对象与数据库的orm字段关系映射
  • 提供对象关系映射标签,支持对象关系组件维护
  • 提供xml标签,支持编写动态sql

第一个Mybatis程序

思路:搭建环境->导入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, '李四', '123890')

新建项目

  1. 新建一个普通的maven项目
  2. 删除src目录
  3. 导入maven依赖(版本自定)

    
        
        
        
            mysql
            mysql-connector-java
            8.0.22
        

        
        
        
            org.mybatis
            mybatis
            3.5.6
        

        
        
        
            junit
            junit
            4.13.2
            test
        
    

编写mybatis工具类:

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

    // 通过factory获得sqlsession
    public static SqlSession getSqlSession() {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        return sqlSession;
    }

}

目录结构:

mybatis配置、用户、用户Dao以及用户Mapper:mybatis-config.xml、User、UserDao、UserMapper.xml




    
        
            
            
                
                
                
                
            
        
    
    
    
        
    

其中,serverTimezone是针对MySQL8的设置。

public class User {
    private int id;
    private String name;
    private String pwd;

    public User() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    @Override
    public String toString() {
        return "User{" + "id=" + id + ", name='" + name + '\'' + ", pwd='" + pwd + '\'' + '}';
    }
}
public interface UserDao {
    List getUserList();
}



    
    

然后编写一个测试类:

public class UserDaoTest {
    @Test
    public void test() {
        // 得到Session
        SqlSession sqlSession = MybatisUtils.getSqlSession();

        // form-1:getMapper
        UserDao mapper = sqlSession.getMapper(UserDao.class);
        List userList = mapper.getUserList();
        for (User user : userList) {
            System.out.println(user);
        }
        sqlSession.close();
    }
}

注意1:此时,会报出找不到xml文件的错误,主要是由于maven导致的问题,应该在pom.xml的配置文件下添加如下语句:


    
        
            
                src/main/resources
                
                    **/*.properties
                    **/*.xml
                
                true
            
            
                src/main/java
                
                    **/*.properties
                    **/*.xml
                
                true
            
        
    

注意2:如果出现“ UTF-8 序列的字节 2 无效。”是因为项目的默认配置编码不是UTF-8。解决方法是:将IDEA设置为UTF-8编码。或者将中文注释的字符去掉。

最终输出为:

User{id=1, name='狂神', pwd='123456'}
User{id=2, name='张三', pwd='123456'}
User{id=3, name='李四', pwd='123890'}

总结:编写Mybatis工具类->写Mybatis配置->写实体类->写实体类Mapper
问题:1、注释带中文字符可能会报错;2、Maven约定大于配置,可能存在找不到配置文件的情况,此时要对Maven进行配置;3、mysql8和mysql5配置存在差异,体现在mybatis的xml配置文件中。

继续深入其他语句(CRUD)

  1. Namespace要对应到Dao;

  2. Select选择查询语句

  • id:对应namespace的方法名
  • resultType:sql返回值
  • parameterType:参数类型

  1. Insert

        insert into mybatis.user (id, name, pwd) values (#{id}, #{name}, #{pwd});

  1. update

        update mybatis.user set name=#{name}, pwd=#{pwd} where id = #{id};

  1. delete

        delete from mybatis.user where id = #{id};

总结:编写接口->编写对应的mapper里的sql语句->测试(增删改需要提交事务)

  1. 万能Map

        insert into mybatis.user (id, name, pwd) values (#{userId}, #{userName}, #{passWord});

  1. 模糊查询

总结:

  1. map传递使用参数为Key名
  2. Object传递使用参数为属性名
  3. 单个基本类型参数可以直接在sql取到
  4. 多个参数用map或者注解
  5. 通配符尽可能固定,需要考虑到注入问题。

配置解析

1、 核心配置文件

  • mybatis-config.xml
  • MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。
  • configuration(配置)
    • properties(属性)
    • settings(设置)
    • typeAliases(类型别名)
    • typeHandlers(类型处理器)
    • objectFactory(对象工厂)
    • plugins(插件)
    • environments(环境配置)
      • environment(环境变量)
        • transactionManager(事务管理器)
        • dataSource(数据源)
    • databaseIdProvider(数据库厂商标识)
    • mappers(映射器)

重点在:properties、environments、mappers等。

  • transactionManager默认为JDBC
  • dataSource默认为POOLED
  • 学会如何配置多套environment
  • properties可以动态替换(使用外部的配置文件比如"db.properties")
  • typeAliases可以给实体类起别名(可以指定包名,找到对应的Beans)
  • Settings里面"logImpl",“useGeneratedKeys”,“mapUnderscoreToCamelCase”等需要了解。
  • Others
    • typeHandlers(类型处理器)
    • objectFactory(对象工厂)
    • plugins(插件)
      • MyBatis-generator-core
      • MyBatis-plus
      • 通用Mapper

配置文件里面可以使用properties来引入外部文件里的属性:(配置的位置需要在Configuration里面的最开始,不然会报错)


    

db.properties的内容(即属性)。(原本的&用&代替,不然会报错)

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF8&serverTimezone=UTC
username=你自己的用户名
password=你自己的密码

typeAliases(别名)的使用

直接指定别名:(实体类少时,推荐使用)


        

指定包名让MyBatis自己去找对应的:(实体类多时,推荐使用)


        

或者在User里面使用注解:

@Alias("User")
public class User

就可以使用User来代替之前的com.pojo.User


Mappers

用class来配置mapper时,接口和配置文件要同名且在同一个包下,用package配置同理。以下是错误示范:


    

解决:应该将UserDao重命名为UserMapper。

最终,可以通过三种方式来配置,任选其一都可以测试成功:


        
        
        

作用域和生命周期

  • 一旦创建了 SqlSessionFactory,就不再需要SqlSessionFactoryBuilder了。(只用一次)
  • SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。(单例)
  • 每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。(用完关闭,如下所示:)
try (SqlSession session = sqlSessionFactory.openSession()) {
  // 你的应用逻辑代码
}

ResultMap(解决对象属性名和数据表字段名不一致的问题)详见:XML 映射器_MyBatis中文网

比如User此时为:

public class User {
    private int id;
    private String name;
    private String password;

属性password与数据库中pwd不同名,此时查询出来的结果如下。

User{id=1, name='狂神', password='null'}
User{id=2, name='张三', password='null'}
User{id=3, name='李四', password='null'}

可见,丢失了最后一个字段,有两种解决方法。

  1. 别名(修改查询语句):
    select id, name, pwd as password from mybatis.user

  2. ResultMap(对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。):设定一个UserMap的resultMap,类型为User。然后,指定方法的resultMap为UserMap,即可完成映射。


        
        
        


总结:

  • ResultMap 元素是 MyBatis 中最重要最强大的元素。
  • ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
  • ResultMap 的优秀之处——你完全可以不用显式地配置它们。

日志

日志工厂

异常排错:sout、debug、日志工厂

使用logImpl,指定 MyBatis 所用日志的具体实现,未指定时将自动查找。可选为:SLF4J(掌握?) | LOG4J(出Bug,掌握) | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING(掌握) | NO_LOGGING。默认无。

对mybatis-config.xml进行配置,注意settings所在的顺序位置。比如下面的命令行LOG。


        

Log4j

是什么(引自百度百科):Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等。我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,通过一个配置文件来灵活地进行配置,精细控制日志的生成过程,而不改代码。

怎么用

  • 先导包:


  log4j
  log4j
  1.2.17

  • 再配置:resources/log4j.properties,以下是一个简单的示例:
# 将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file

# 控制台输出的相关设置
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

# 文件输出的相关设置
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/kuang.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%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
  • 再把settings改成使用LOG4J:

        

  • 使用:
static Logger logger = Logger.getLogger(userDaoTest.class); // 当前类
@Test
public void testLog4j(){ //多种日志的级别
  logger.info("info:进入了testlog4j");
  logger.debug("debug:进入了testlog4j");
  logger.warn("warn:进入了testlog4j");
  logger.error("error:进入了testlog4j");
}
  • 输出:
[dao.userDaoTest]-info:进入了testlog4j
[dao.userDaoTest]-debug:进入了testlog4j
[dao.userDaoTest]-warn:进入了testlog4j
[dao.userDaoTest]-error:进入了testlog4j

分页

目的:减少运算数据量。

1、 LIMIT实现分页

实现sql的相关接口。

List getUserByLimit(Map map);

进行测试。

@Test
public void testGetUserByLimit(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    HashMap parameterMap = new HashMap<>();
    parameterMap.put("startIndex", 1);
    parameterMap.put("endIndex", 2);
    List userByLimit = mapper.getUserByLimit(parameterMap);
    for (User user: userByLimit) {
        System.out.println(user);
    }
    sqlSession.close();
}

2、RowBounds实现分页

List getUserByRowBounds();

@Test
public void testGetUserByRowBounds() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    // Java层面来直接selectList
    RowBounds rowBounds = new RowBounds(1, 2);
    List userList = sqlSession.selectList("com.dao.UserMapper.getUserByRowBounds", null, rowBounds);
    for (User user : userList) {
        System.out.println(user);
    }
    sqlSession.close();
}

3、分页插件
PageHelper等。

使用注解开发

面向接口编程

目的:解耦

  • 个体的抽象:abstract class
  • 个体的某个方面的抽象:interface

三个面向

  • 面向对象:考虑对象的属性和方法。
  • 面向过程:以流程(事务)考虑实现。
  • 接口设计与非接口设计:与另外两个考虑的不是一个问题,更多的是对系统整体的架构。

注解开发

可以不配置对应mapper的xml,使用简单的注解。

@Select("select * from user")
List getUsers();

然后,绑定接口:



        

@Test
public void testGetUsers() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List users = mapper.getUsers();
    for (User user : users) {
        System.out.println(user);
    }
    sqlSession.close();
}
  • 实现:反射
  • 底层:动态代理

MyBatis执行流程

加载配置->SqlSessionFactoryBuilder(构建XMLConfigBuilder->转换Configuration)->SqlSessionFactory->(transaction事务管理->executor->sqlSession->CRUD->是否执行成功)->提交事务->关闭

继续CRUD

增改删(注解版)及测试:

@Insert("insert into user(id, name, pwd) values (#{id},#{name},#{password})")
int addUser(User user);

@Update("update user set name=#{name}, pwd=#{password} where id = #{id}")
int updateUser(User user);

@Delete("delete from user where id = #{id}")
int deleteUser(int id);
@Test
public void testAddUser() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    int res = mapper.addUser(new User(6, "aaa", "1234555"));
    if (res > 0){
        System.out.println("插入成功");
    }
    sqlSession.commit();
    sqlSession.close();
}

@Test
public void testUpdateUser() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    int res = mapper.updateUser(new User(6, "aaa", "6666"));
    if (res > 0){
        System.out.println("改变成功");
    }
    sqlSession.commit();
    sqlSession.close();
}

@Test
public void testDeleteUser() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    int res = mapper.deleteUser(6);
    if (res > 0){
        System.out.println("删除成功");
    }
    sqlSession.commit();
    sqlSession.close();
}

关于@Param注解

  • String或者基本类的需要加上
  • 引用类型不需要加
  • 只有一个基本类型可以忽略
  • SQL中引用的就是@Param中的属性名

#{}和${}

  • $直接拼接,无法防止注入
  • $多用在传入数据库参数时
  • 尽量使用#

Lombok!

是什么:Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java.
Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your logging variables, and much more.

  • 通过IDEA进行插件安装
  • 导包

    
        org.projectlombok
        lombok
        1.16.16
        provided
    

Lombok注解内容:

@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 config system
Code inspections
Refactoring actions (lombok and delombok)

  • @Data:无参构造,get,set,toString,hashcode,equals
  • @AllArgsConstructor:有参构造
  • @NoArgsConstructor:无参构造
  • @Getter
  • @Setter
  • @ToString

复杂查询环境

  • 多个学生对应/关联一个老师(多对一)
  • 集合,一个老师有很多学生(一对多)

先建表搭建环境:

CREATE TABLE `teacher` (
  `id` INT(10) NOT NULL,
  `name` VARCHAR(30) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老师'); 

CREATE TABLE `student` (
  `id` INT(10) NOT NULL,
  `name` VARCHAR(30) DEFAULT NULL,
  `tid` INT(10) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `fktid` (`tid`),
  CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小红', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小张', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');

流程:导入lombok->建立实体类->建立Mapper接口->建立Mapper.xml文件->在核心配置文件中绑定Mapper->测试查询是否成功。

多对一

按照查询嵌套处理

查找学生的时候,通过外键查询对应的老师。

    

    
        
        

        
    

    

按照结果嵌套处理

    

    
        
        
        
            
            
        
    

一对多

按照结果嵌套处理

    

    
        
        

        
            
            
            
        
    

按照查询嵌套处理

    

    
        
        

        
    

    

总结:

  • 保证SQL可读性
  • 一对多和多对一的字段和属性名的对应问题
  • 问题如果不好排查,使用日志系统,如:Log4j
  • 最终SQL优化的方向为:学习和理解Mysql引擎、InnoDB底层原理、索引、索引优化!

动态SQL

是什么:根据不同条件生成SQL语句。

搭建环境:

CREATE TABLE `blog`(
`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;
    @Test
    public void testAddBlogs() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

        Blog blog = new Blog();
        blog.setId(IDutils.getID());
        blog.setAuthor("KyoDante");
        blog.setCreateTime(Timestamp.from(Instant.now()));
        blog.setTitle("Mybatis如此简单");
        blog.setViews(7777);
        mapper.addBlog(blog);

        blog.setId(IDutils.getID());
        blog.setTitle("Spring如此简单");
        blog.setCreateTime(Timestamp.from(Instant.now()));
        mapper.addBlog(blog);

        blog.setId(IDutils.getID());
        blog.setTitle("Java如此简单");
        blog.setCreateTime(Timestamp.from(Instant.now()));
        mapper.addBlog(blog);

        blog.setId(IDutils.getID());
        blog.setTitle("微服务如此简单");
        blog.setCreateTime(Timestamp.from(Instant.now()));
        mapper.addBlog(blog);

        sqlSession.commit();

        sqlSession.close();
    }
public class IDutils {
    public static String getID() {
        return UUID.randomUUID().toString().replace("-", "");
    }
}

IF

    

常用标签

where和if搭配(只要满足就填入SQL)

    

Choose(类似Switch,多选一)

    

Trim

与 set 元素等价的自定义 trim 元素:


  ...

sql和include

  • 用sql抽取复用的部分
    
        
            and title = #{title}
        
        
            and author = #{author}
        
    
  • 用include将该片段包括进去
    

FOREACH

    

缓存

是什么:查询连接数据库,耗资源,存到某些地方,下次可以直接使用!内存->缓存(解决高并发性能问题)
为什么:减少和数据库的交互开销,提高系统效率
哪里使用:经常查询且不经常改变的数据。

  • Mybatis有一级缓存二级缓存
  • 默认为开启一级缓存。(SqlSession级别的缓存;也称为本地缓存)
  • 二级缓存是手动开启和配置,基于namespace级别的缓存
  • 为了提高扩展性,定义了缓存接口Cache,可以实现它来定义二级缓存

一级缓存

    @Test
    public void test() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.queryUserById(1);

        System.out.println("==================");
        User user2 = mapper.queryUserById(1);

        System.out.println(user2);
        System.out.println(user);
        System.out.println(user == user2);
        
        sqlSession.close();
    }
  • 增删改会刷新缓存
    @Test
    public void test() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.queryUserById(1);

        User user3 = new User();
        user3.setId(2);
        user3.setName("aaaa");
        user3.setPwd("bbbb");
        int res = mapper.updateUserById(user3);
        if (res > 0) {
            System.out.println("改变成功");
        }
        System.out.println("==================");
        User user2 = mapper.queryUserById(1);

        System.out.println(user2);
        System.out.println(user);
        System.out.println(user == user2);

        sqlSession.close();
    }
  • 或者清除缓存
    sqlSession.clearCache();

二级缓存

为了什么:一级缓存只在SqlSession作用域内,会话没了,还会存在于二级缓存中。新的会话可以从二级缓存中获得内容。不同mapper放在对应的map中。

在Mapper中设置(如果直接这么设置,需要序列化实体类,即implements Serializable)


在核心配置中使用(默认开启):


  • 最开始是一级缓存
  • 只有在关闭会话,或者提交会话的时候,才会转到二级缓存

缓存原理

提高查询效率
从用户的角度:先查二级再查一级。
从程序的角度:先从SqlSession,也即一级缓存;再到Mapper,也即二级缓存。

EhCache

  • 导包

        
            org.mybatis.caches
            mybatis-ehcache
            1.2.1
        
  • 配置resources/ehcache.xml



    

    

    

使用


Redis:K-V来实现缓存。(现在常用)

你可能感兴趣的:(Mybatis笔记)