MyBatis 使用 Redis 缓存

前言

在MyBatis 缓存一节中,我们介绍了 MyBatis 的多级缓存。MyBatis 的二级缓存可在多个会话中共享缓存,但是这也加大了内存的使用空间,如果二级缓存空间占有量过多势必会导致程序运行空间的不足,因此我们需要将二级缓存转移到专业的缓存服务器上。

Redis 是一个高性能的 kv 数据库,被广泛的使用在缓存服务上,MyBatis 项目开发者提供了 Redis 缓存的实现。本小节我们将一起来学习如何在 MyBatis 中集成 Redis 缓存。

准备

添加依赖

在项目的 pom.xml 文件中添加上 mybatis-redis 依赖:

1
2
3
4
5
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-redis</artifactId>
<version>1.0.0-beta2</version>
</dependency>

mybatis-redis 目前还只有 beta 版,相信不久后就可以到正式版了。

Redis服务器

请确保你拥有一台可用的 Redis 服务器,并能够正常运行。如下:

1
2
127.0.0.1:6379> ping
PONG

Redis配置文件

在 src/main/resources 目录下新增 redis.properties 配置文件,并向其中添加如下配置:

1
2
3
4
5
6
7
host=localhost
port=6379
connectionTimeout=5OOO
soTimeout=5OOO
password=123456
database=O
clientName=

注意,请根据自己的环境来修改配置,如密码和主机。

使用

配置

在对应的 mapper 配置文件中,如 UserMapper.xml 文件添加上对应的缓存配置。如下:

1
<cache type="org.mybatis.caches.redis.RedisCache" />

调用

配置好以后,我们就可以直接在程序中调用了。

1
2
3
4
5
6
7
8
SqlSession session1 = sqlSessionFactory.openSession();
UserMapper userMapper1 = session1.getMapper(UserMapper.class);
User user1 = userMapper1.selectUserById(1);
System.out.println(user1);
SqlSession session2 = sqlSessionFactory.openSession();
UserMapper userMapper2 = session2.getMapper(UserMapper.class);
User user2 = userMapper2.selectUserById(1);
System.out.println(user2);

注意,User 对象必须实现Serializable接口才可被缓存。 比如:

1
public class User implements Serializable {}

当缓存成功,程序会有如下输出,表示缓存击中。

1
2
3
4
20:58:12.462 [main] DEBUG com.ahao.mybatis.mapper.UserMapper - Cache Hit Ratio [com.ahao.mybatis.mapper.UserMapper]: 1.0
User{id=1, username='peter', age=18, score=100}
20:58:12.499 [main] DEBUG com.ahao.mybatis.mapper.UserMapper - Cache Hit Ratio [com.ahao.mybatis.mapper.UserMapper]: 1.0
User{id=1, username='peter', age=18, score=100}

提示: 在第一次运行时,会向 Redis 中存放数据,不会使用到缓存,第二次运行时则才会使用到缓存。

小结

  • Redis 缓存十分常见,MyBatis 集成 Redis 缓存也异常方便,但是在实际生产环境下如此应用的都不多,而是直接选择其它方式来使用 Redis 缓存。

spring-boot 集成 MyBatis

前言

spring-boot 可谓是 Java 领域中最火的框架之一, 也是目前 Java 应用开发的事实标准,如果不会使用未免有些 out。本小节,我们将一起学习如何使用 spring-boot 来集成 MyBatis。

项目初始化

对于拥有 IDEA 专业版的人来说,新建 spring-boot 项目是十分方便的,当然如果你没有也没关系,Spring 官方给我们提供了一个快速新建项目的网站。

打开 https://start.spring.io/ 。如下:
图片描述

给项目输入合适的 groupId 和 artifactId 然后点击生成按钮,浏览器会自动下载一个压缩包 springboot-mybatis-exmaple.zip。

解压该包,并通过 IDE 打开这个项目。

启动器

在项目的 pom 文件中添加如下 3 个依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>

mybatis-spring-boot-starter 是 MyBatis 为 spring-boot 提供的启动器,添加后就可以以少量的配置来快速的使用 MyBatis;spring-boot-starter-web 是 spring-boot 提供的 Web 启动器,会给我们提供一个 Web 应用环境;mysql-connector-java 是 MySQL 数据库驱动依赖。

配置

在 src/main/resources 有 spring-boot 提供的默认配置文件 application.properties。在该配置文件下,我们需要添加上对于的数据源配置。

1
2
3
4
5
# 数据源配置,请修改为你项目的实际配置
spring.datasource.url=jdbc:mysql://localhost:3306/imooc
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

实践

模型层

在项目的 com.ahao.mybatis.springbootmybatisexample 的包下新建 model 包,并在该包下新建 User 类。如下:

1
2
3
4
5
6
7
public class User implements Serializable {
private Long id;
private String username;
private Integer age;
private Integer score;
// 省略 getter 和 setter 方法
}

User 类与数据表 imooc_user 对应。

数据访问层

在 com.ahao.mybatis.springbootmybatisexample 包下新建 mapper 包,并新建 UserMapper 类。如下:

1
2
3
4
5
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper {
}

控制层

在 com.ahao.mybatis.springbootmybatisexample 包下新建 controller 包,并新建 UserController 类。如下:

1
2
3
4
5
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {
}

使用 RestController 注解将 UserController 标记为控制器。

完善

有了雏形后,我们再为这些类添加上具体的业务,比如查询所有 imooc_user 用户。

  1. 在 UserMapper 中,我们添加上 getUsers 接口方法,并通过 Select 注解来映射相应的 SQL 语句。

    1
    2
    3
    4
    5
    6
    @Mapper
    @Repository
    public interface UserMapper {
    @Select("SELECT * FROM imooc_user")
    List<User> getUsers();
    }
  2. 在 UserController 中,我们添加上对应的 getUsers API,该 API 对外暴露所有用户数据。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @RestController
    @RequestMapping("/user")
    public class UserController {
    @Autowired
    private UserMapper userMapper;

    @GetMapping
    public List<User> getUsers() {
    return userMapper.getUsers();
    }
    }

测试

运行项目,如果你使用 IDE,可直接通过按钮来运行,如果没有也可使用终端来运行程序:

1
mvn spring-boot:run 

运行成功后,我们测试一下:

1
2
3
4
5
curl 127.0.0.1:8080/user/
代码块1
[{"id":1,"username":"peter","age":18,"score":100},
{"id":2,"username":"pedro","age":24,"score":200},
{"id":3,"username":"jerry","age":28,"score":500}]

小结

  • spring-boot 集成 MyBatis 是十分方便的,只需少量的配置就可轻松使用 MyBatis。
  • spring-boot + MyBatis 几乎是国内标配,请务必要上手实操一下。

使用 MyBatis-tk 和 pagehelper

前言

在 spring-boot 集成 MyBatis小节中,我们介绍了如何在 spring-boot 中集成 MyBatis,MyBatis 虽然灵活,但是对于业务开发还略显不够。tk.mapper 和 pagehelper 是国内开发者为 MyBatis 定制的两款业务增强库,tk.mapper 可以让开发者从基本的增删查改中解放,pagehelper 则提供了高度易用的分页功能。

有了它们,MyBatis 可谓是如虎添翼,本小节我们将介绍这两款库的使用。

tk.mapper

简介

在 tk.mapper 项目的首页有关于它最精确的介绍。

通用 mapper 是一个可以实现任意 MyBatis 通用方法的框架,项目提供了常规的增删改查操作以及 example 相关的单表操作。通用 mapper 是为了解决 MyBatis 使用中 90% 的基本操作,使用它可以很方便的进行开发,可以节省开发人员大量的时间。

总结来说,tk.mapper 可以为 MyBatis 开发节省大量的时间,精简的 90% 的基本操作。

依赖

tk.mapper 的使用也十分简单,我们只需引入相应的 starter 即可。如下:

1
2
3
4
5
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.1.5</version>
</dependency>

TIPS: 注意,引入了 mapper starter 后,虽然和 MyBatis 官方的 starter 没有冲突,但是官方的自动配置不会生效!

使用

简单地说,tk.mapper 会给普通的 mapper 做一些增强,让 mapper 具有基本的增删查改的能力。如下:

1
2
3
4
5
6
7
8
import tk.mybatis.mapper.common.BaseMapper;

@Mapper
@Repository
public interface UserMapper extends BaseMapper<User> {
@Select("SELECT * FROM imooc_user")
List<User> getUsers();
}

UserMapper 是原有的 mapper,只需继承 tk.mapper 中的 BaseMapper 就完成的增强。

UserMapper 继承 BaseMapper 后就拥有了基础的增删查改功能,而无需写 SQL。BaseMapper 必须指定一个与 UserMapper 对应的模型类,即 User 类。

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.ahao.mybatis.springbootmybatisexample.model;

import javax.persistence.Table;
import java.io.Serializable;

@Table(name = "imooc_user")
public class User implements Serializable {
private Long id;
private String username;
private Integer age;
private Integer score;
// 省略 getter 和 setter 方法
}

数据表的名称为 imooc_user,这里我们通过 Table 注解来告诉 tk User 模型所对应的数据表。

mapper 增强后,我们还需要给 SpringbootMybatisExampleApplication 启动类添加上一个注解:

1
2
3
4
5
6
7
8
9
import tk.mybatis.spring.annotation.MapperScan;

@SpringBootApplication
@MapperScan("com.ahao.mybatis.springbootmybatisexample.mapper")
public class SpringbootMybatisExampleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootMybatisExampleApplication.class, args);
}
}

MapperScan 是 tk 提供的一个 mapper 扫描注解,在注解中我们需要填入 mapper 所在的包路径,即 com.ahao.mybatis.springbootmybatisexample.mapper。

到此 UserMapper 增强已经完成了,我们尝试在 UserController 中使用。

1
2
3
4
5
6
7
8
9
10
11
12
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserMapper userMapper;

@GetMapping
public List<User> getUsers() {
// return userMapper.getUsers();
return userMapper.selectAll();
}
}

在这里,我们替换 getUsers 方法,而是使用了 selectAll 方法,selectAll 是 BaseMapper 提供的方法,也就是增强的方法,我们无需去为这个方法添加相应的 SQL,tk 会自动帮我们搭理好一切。

再次运行程序,并测试接口:

1
2
# curl 127.0.0.1:8080/user/
[{"id":1,"username":"peter","age":18,"score":100},{"id":2,"username":"pedro","age":24,"score":200},{"id":3,"username":"jerry","age":28,"score":500},{"id":4,"username":"mike","age":12,"score":300}]

我们得到了与之前一样的结果,可以看到在我们并未书写任何 SQL 的前提下, tk.mapper 自动帮我们生成了需要的 SQL 查询,这就是 tk.mapper 所带来的简化开发能力。

pagehelper

简介

pagehelper 是一个 方便好用的 MyBatis 分页插件,支持复杂的单表、多表分页,是目前 MyBatis 中使用最为广泛的插件之一。

依赖

和其它一样,pagehelper 也只需导入相应的启动器即可。

1
2
3
4
5
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.13</version>
</dependency>

使用

pagehelper 充分的简单易用,在 UserController 中只需添加一行代码即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import com.github.pagehelper.PageHelper;

@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserMapper userMapper;

@GetMapping
public List<User> getUsers() {
PageHelper.startPage(1, 2);
return userMapper.selectAll();
}
}

PageHelper.startPage(1, 2)会自动拦截后面将要执行的 SQL,然后处理该 SQL 并添加分页参数。startPage 接收两个参数,第一个参数表示当前分页,第二个参数表示分页数目,如这里的 1,2 表示第一页且该页最多有两条记录。

运行程序,并测试接口:

1
2
# curl 127.0.0.1:8080/user/
[{"id":1,"username":"peter","age":18,"score":100},{"id":2,"username":"pedro","age":24,"score":200}]

与上面的结果相比,结果的数目只有两条,显然分页插件已经生效。

TIPS: 提示,pagehelper 的分页是从1开始的,而有些分页插件分页是从0开始的。

小结

  • 有了 spring-boot 后,很多项目的开发都将变得简单,mapper 和 pagehelper 都只需添加一个依赖即可使用。
  • 本小节简单的介绍了 mapper 和 pagehelper,如果你想深入了解它们,可以阅读它们的文档:pagehelpermapper

使用 MyBatis-plus

前言

在spring-boot 集成 MyBatis小节中,我们介绍了如何在 spring-boot 中集成 MyBatis,MyBatis 虽然灵活,但是对于业务开发还略显不够。MyBatis-Plus 是国内开发者为 MyBatis 定制的一款增强工具,在不侵入 MyBatis 的基础上能够快速地提升 MyBatis 的开发能力,为开发者节省大量的时间。

简介

MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

MyBatis-Plus 内置丰富的基础 mapper 和插件,是目前国内使用最为广泛的开源项目之一。

依赖

在 spring-boot 的基础上,使用 MyBatis-Plus 也十分简单,只需在 pom 文件中添加上对应的 starter 即可。

1
2
3
4
5
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1.tmp</version>
</dependency>

准备

MP 提供了十分方便的 BaseMapper 供项目 mapper 继承,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.ahao.mybatis.springbootmybatisexample.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ahao.mybatis.springbootmybatisexample.model.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;
import java.util.List;

@Mapper
@Repository
public interface UserMapper extends BaseMapper<User> {

@Select("SELECT * FROM imooc_user")
List<User> getUsers();
}

UserMapper 继承 BaseMapper 后就拥有了基础的增删查改功能,而无需写 SQL。BaseMapper 必须指定一个与 UserMapper 对应的模型类,即 User 类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.ahao.mybatis.springbootmybatisexample.model;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;

@TableName("imooc_user")
public class User implements Serializable {
@TableId("id")
private Long id;
private String username;
private Integer age;
private Integer score;
// 省略诸多 getter 和 setter 方法
}

在模型类中,我们需要通过 TableName 注解指定模型对于的数据表名称,并通过 TableId 注解指定数据表字段。

并在 SpringbootMybatisExampleApplication 启动类上添加 MapperScan 注解来扫描 mapper,如下:

1
2
3
4
5
6
7
8
9
10
11
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.ahao.mybatis.springbootmybatisexample.mapper")
public class SpringbootMybatisExampleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootMybatisExampleApplication.class, args);
}
}

MapperScan 中需填入 mapper 所在的包路径,即 com.ahao.mybatis.springbootmybatisexample.mapper。

使用

基础使用

接下来,我们在 UserController 中来使用增强后的 UserMapper。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.ahao.mybatis.springbootmybatisexample.controller;

import com.ahao.mybatis.springbootmybatisexample.mapper.UserMapper;
import com.ahao.mybatis.springbootmybatisexample.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;

@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserMapper userMapper;

@GetMapping
public List<User> getUsers() {
// return userMapper.getUsers();
return userMapper.selectList(null);
}
}

这里,我们替换了之前使用的 getUsers 方法,而是使用了 BaseMapper 提供的 selectList 方法,该方法接收一个参数,这里我们暂时定为 null。

运行程序,并通过 curl 测试接口:

1
2
# curl 127.0.0.1:8080/user/
[{"id":1,"username":"peter","age":18,"score":100},{"id":2,"username":"pedro","age":24,"score":200},{"id":3,"username":"jerry","age":28,"score":500},{"id":4,"username":"mike","age":12,"score":300}]

我们仍然取得了与之前一样的结果。可以看到,MP 在增强 UserMapper 后,我们无需再写多余的 SQL,MP 自动的为我们生成了对应的 SQL 语句。

进阶使用

MP 的功能远不止如此简单,我们还可以通过 Java API 给 selectList 函数传入 SQL 筛选条件。如,我们只想获取分数大于 200 分的用户。

1
2
3
4
5
6
7
8
9
10
11
12
13
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserMapper userMapper;

@GetMapping
public List<User> getUsers() {
QueryWrapper<User> condition = new QueryWrapper<>();
condition.lambda().gt(User::getScore, 200);
return userMapper.selectList(condition);
}
}

QueryWrapper 是 MP 提供的查询条件包装器,通过 Java API 的方式我们就可以构造 SQL 过滤条件。如这里我们通过 lamdba 表示式构造了用户积分需大于200的过滤条件,然后将包装器以参数的形式传递给 selectList 函数。

再次运行程序,并通过 curl 测试接口:

1
2
# curl 127.0.0.1:8080/user/
[{"id":3,"username":"jerry","age":28,"score":500},{"id":4,"username":"mike","age":12,"score":300},{"id":5,"username":"tom","age":27,"score":1000}]

从结果中,可以看出积分小于200的用户都已经被过滤掉了。

分页插件

MP 内置提供了分页插件,只需几行代码我们就可直接引入。

1
2
3
4
5
6
7
8
9
10
11
12
13
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;

@SpringBootApplication
@MapperScan("com.ahao.mybatis.springbootmybatisexample.mapper")
public class SpringbootMybatisExampleApplication {
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
public static void main(String[] args) {
SpringApplication.run(SpringbootMybatisExampleApplication.class, args);
}
}

在 SpringbootMybatisExampleApplication 启动类中,我们添加了一个带有 Bean 注解的 paginationInterceptor 方法,该方法返回一个 PaginationInterceptor 对象,这样插件就配置完成了。

接下来,我们来使用它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserMapper userMapper;

@GetMapping
public List<User> getUsers() {
Page page = userMapper.selectPage(new Page<>(1, 3), null);
return page.getRecords();
}
}

UserMapper 的 selectPage 方法接受两个参数,第一个参数是分页配置,第二个参数是查询条件包装器。这里第一个参数,我们直接通过 Page 构造函数来构造,1 表示当前页,3 表示分页最大的记录数,即第一页且最多返回三条记录;第二个参数我们直接设置为 null。

运行程序,并通过 curl 测试接口:

1
2
# curl 127.0.0.1:8080/user/
[{"id":1,"username":"peter","age":18,"score":100},{"id":2,"username":"pedro","age":24,"score":200},{"id":3,"username":"jerry","age":28,"score":500}]

从结果可以看出,分页生效了,记录总数却是为 3 条。

小结

  • MyBatis-Plus 是国内使用十分广泛的一款 ORM 库,极大的节省了开发者的开发时间,功能强大且使用简单。
  • MyBatis-Plus 的内容还是较多的,如果你感兴趣,不妨访问它的官网看一看。