MyBatis-Plus
(简称 MP)是一个 MyBatis
的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
正如官方文档中所提到的,Mybatis-Plus拥有很多优秀的特性:
Mybatis-Plus 作为更优秀的持久层框架,继续沿用了 ORM
的思想,也支持很多的数据库连接
官方也给出了免费的第三方视频可供学习:
MyBatis-Plus 入门 - 视频教程 - 慕课网
MyBatis-Plus 进阶 - 视频教程 - 慕课网
Mybatis-Plus
的知识并不少,不是三言两语能够讲清楚的。这里选取了 Mybatis-Plus
的部分基础要点与 SpringBoot
进行整合学习,以练促学,达到以点带面的效果。
如果想要更近一步的学习 Mybatis-Plus
的高级应用,还是拿官网文档学习为最佳。
使用 IDEA
工具新建 SpringBoot
项目,初始化组件选择 lombok
、Spring Web
、MySQL Driver
。
在 pom.xml
文件中引入 Mybatis-Plus
依赖、阿里巴巴的 druid
连接池
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.2.0version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.19version>
dependency>
新建 dao 目录,在主程序中配置 Mapper 扫描
@SpringBootApplication
@MapperScan("com.example.dao")
public class SpringbootMybatisplusApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootMybatisplusApplication.class, args);
}
}
使用 Navicat
在 MySQL 中新建一个名为 mybatisplus
的数据库
新建 application.yml
文件,进行基本配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatisplus? useUnicode=true & useSSL=false &
characterEncoding=utf-8 & serverTimezone=Asia/Shanghai
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
# 配置日志
logging:
level:
root: info
com:
example:
dao: debug
这里因为 Mybatis-plus 内置了很多 sql 的增删改查方法,足够我们应付大多数的应用场景,(相当于提供了零 xml 配置的特性),所以我们不需要再通过 mapper-locations
进行 xml 文件的指定,如果有特殊需要再去做。同时,我们也不用通过 type-aliases-package:
对实体类器别名,mybatis-plus 会自动为实体类起别名。
我们去 mybatisplus
数据库中新建一个 user
表
DROP TABLE IF EXISTS user;
CREATE TABLE user(
id VARCHAR(11) NOT NULL AUTO_INCREMENT,
name VARCHAR(25) DEFAULT NULL,
age INT(11) DEFAULT NULL,
bir TIMESTAMP NULL DEFAULT NULL,
PRIMARY KEY(id)
)ENGINE=INNODB DEFAULT charset=utf8;
随便插入几条数据
新建 entity 目录,目录下新建 User 类
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class User {
private String id;
private String name;
private Integer age;
private Date bir;
}
@Accessors
是 lombok 的一个注解,翻译为存取器,通过该注解可以控制getter和setter方法的形式。chain 为 true,则setter方法返回当前对象。
//没加Accessors(chain = true)
public void setName(String name) {
this.name = name;
}
//加了Accessors(chain = true)
public User setName(String name) {
this.name = name;
return this;
}
在 dao 目录下新建 UserDao 接口。
我们要想在 Dao 层接口中使用 mybatis-plus
的增强,只需要继承 BaseMapper<实体类>
,就可以调用 mybatis-plus 封装好的增删改查方法。
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.entity.User;
import org.springframework.stereotype.Repository;
//使用mybatisplus增强结果
public interface UserDao extends BaseMapper<User> {
}
我们通过测试类进行测试
如果 userDao 标红,提示说不能自动装配也没有关系,测试类也是可以运行起来的。
@SpringBootTest
class SpringbootMybatisplusApplicationTests {
@Autowired
private UserDao userDao;
//查询所有
@Test
public void testFindAll(){
List<User> users = userDao.selectList(null);//参数为空默认查询所有数据
// lambda表达式
users.forEach(user -> System.out.println("user = "+ user));
}
}
可以看到,我们已经轻松查询到了 user 表中的数据
我们在使用 mybatis-plus 的时候,最常用的是三个注解
value
:String类型,指定映射的表名(如果为空则默认将类名用来映射表名)resultMap
:String类型,用来指定 xml 配置中 resultMap 的 id 值(仅限于自己使用复杂resultMap时使用)value
:String类型,指定实体类中与表中对应的主键列名type
:枚举类型,指定主键生成类型IdType
值 | 描述 |
---|---|
AUTO | 数据库ID自增 |
NONE | 无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT) |
INPUT | insert前自行set主键值 |
ASSIGN_ID | 分配ID(主键类型为Number(Long和Integer)或String)(since 3.3.0), 使用接口 IdentifierGenerator 的方法nextId (默认实现类为 DefaultIdentifierGenerator 雪花算法) |
ASSIGN_UUID | 分配UUID,主键类型为String(since 3.3.0), 使用接口 IdentifierGenerator 的方法nextUUID (默认default方法) |
对于我们刚才建立的实体类来说,我们也可以这样编写
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@TableName("user")//数据表名
public class User {
@TableId(value = "id", type = IdType.AUTO)//自增主键
private String id;
@TableField(value = "name")
private String name;
private int age;
private Date bir;
@TableField(exist = false)//不映射数据表中的任何字段
private String email;
}
重新运行测试类:
//查询所有
@Test
public void testFindAll(){
List<User> users = userDao.selectList(null);//参数为空默认查询所有数据
users.forEach(user -> System.out.println("user = "+ user));
}
//根据主键查询一个
@Test
public void testFindById(){
User user = userDao.selectById(1);
System.out.println("user = "+user);
}
//条件查询
@Test
public void testFind(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("age", 23);//设置等值查询
//queryWrapper.lt("age", 23);//设置小于查询
//queryWrapper.le("age", 23);//设置小于等于查询
//ne对应不等于,gt对应大于,ge对应大于等于······
List<User> users = userDao.selectList(queryWrapper);
users.forEach(user -> System.out.println("user = "+user));
}
//模糊查询
@Test
public void testFindLike(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// like %?%
queryWrapper.like("name", "o");
//likeLeft %? 以xxx结尾的
//likeRight ?% 以xxx开头的
List<User> users = userDao.selectList(queryWrapper);
users.forEach(user -> System.out.println("user = "+ user));
}
模糊查询的结果为:
//增加一条记录
@Test
public void testInsert(){
User user = new User();
user.setName("Jerry").setAge(23).setBir(new Date());
userDao.insert(user);
}
可以看到,数据表中已经成功添加了一条记录
//基于主键id进行数据修改
@Test
public void testUpdateById(){
//修改3号的名字为Kafka
User user = userDao.selectById(3);
user.setName("Kafka");
userDao.updateById(user);
}
//批量修改
@Test
public void testUpdate(){
//将age为23的所有人名字修改为Chris
User user = new User();
user.setName("Chris");
QueryWrapper<User> updateWrapper = new QueryWrapper<>();
updateWrapper.eq("age", 23);
userDao.update(user, updateWrapper);
}
可以看到,第一个更新方法将 3 号的名字更新为 Kafka,第二个更新方法将年龄为23的两个人名字改成了 Chris
//基于Id删除一条记录
@Test
public void testDeleteById() {
userDao.deleteById(3);
}
//基于条件进行删除
@Test
public void testDelete(){
//删除年龄>=23的所有人
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.ge("age", "23");
userDao.delete(queryWrapper);
}
可以看到,第一个方法删除了3号记录,第二个方法删除了年龄>=23的所有人
注意,当前 Mybatis-Plus 的分页插件只支持单表查询,不支持多表连接查询。
我们刚才测试删除方法时已经将数据删除得所剩无几了,先往里插入几条数据
新建 config 目录,在目录下新建 UserPage
类
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@EnableTransactionManagement
@Configuration
@MapperScan("com.example.dao")
public class UserPage {
//分页的拦截器依赖
@Bean
public PaginationInterceptor paginationInterceptor(){
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
return paginationInterceptor;
}
}
继续在测试类中进行测试:
//不带条件分页查询
@Test
public void testPage(){
//参数1:当前页,默认是1
// 参数2:每页显示记录数,默认为10
IPage<User> page = new Page<>(1, 2);
IPage<User> userIPage = userDao.selectPage(page, null);
//查看总数,并遍历
long total = userIPage.getTotal();
System.out.println("总记录数:" + total);
userIPage.getRecords().forEach(user -> System.out.println("user = " + user));
}
//带条件分页查询
@Test
public void testPage2(){
IPage<User> page = new Page<>(1, 2);
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//根据返回年龄为20的记录做分页
queryWrapper.eq("age", 23);
IPage<User> userIPage = userDao.selectPage(page, queryWrapper);
//查看总数,并遍历
long total = userIPage.getTotal();
System.out.println("总记录数:" + total);
userIPage.getRecords().forEach(user -> System.out.println("user = " + user));
}
为了确保数据库产品的稳定性,很多数据库拥有双机热备份功能。
这里我们就不用服务器进行演示了,直接用本机上两个不同的数据库来模拟不同的 MySQL 服务。
dynamic-datasource-spring-boot-starter
是一个基于 springboot 的快速集成多数据源的启动器,我们需要在 pom.xml
文件中引入依赖
<dependency>
<groupId>com.baomidougroupId>
<artifactId>dynamic-datasource-spring-boot-starterartifactId>
<version>3.0.0version>
dependency>
在 application.properties
文件中配置数据源(清空 application.yml)
spring.datasource.primary=master # 指定默认数据源
spring.datasource.dynamic.datasource.master.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.dynamic.datasource.master.url=jdbc:mysql://localhost:3306/mybatisplus? \
useUnicode=true & characterEncoding=utf-8 & serverTimezone=Asia/Shanghai
spring.datasource.dynamic.datasource.master.username=root
spring.datasource.dynamic.datasource.master.password=123456
# 一主一丛,主数据源名称为 master,从数据源名称为 slave_1
# 一主多从同理
spring.datasource.dynamic.datasource.slave_1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.dynamic.datasource.slave_1.url=jdbc:mysql://localhost:3306/mybatisplus2? \
useUnicode=true & characterEncoding=utf-8 & serverTimezone=Asia/Shanghai
spring.datasource.dynamic.datasource.slave_1.username=root
spring.datasource.dynamic.datasource.slave_1.password=123456
在 Navicat
中新建一个数据库 mybatisplus2
作为从库,将主库中的 user 表复制过来,保证主库和从库的数据一致。
当我们要进行数据的业务切换时,需要在业务层进行,在 Dao 层是不行的。
新建 service
目录,目录下新建 UserService
接口
public interface UserService {
//查询方法
List<User> findAll();
//添加方法
void insert(User user);
}
在同级目录下,新建 UserServiceImpl
类,借助 Dao 层实现这个接口
import com.example.dao.UserDao;
import com.example.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@Transactional
public class UserServiceImpl implements UserService{
@Autowired
private UserDao userDao;
@Override
public List<User> findAll() {
return userDao.selectList(null);
}
@Override
public void insert(User user) {
userDao.insert(user);
}
}
在 test 包下,新建一个测试类 TestUserService
import com.example.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class TestUserService {
@Autowired
private UserService userService;
@Test
public void testFindAll(){
userService.findAll().forEach(user -> System.out.println("user = "+ user));
}
}
运行 testFindAll()
方法,会默认从主库中取出数据
dynamic
这个启动器为我们引入了 @DS
这个注解
@DS:
现在主库和从库的数据是一致的,即使切换了也看不出来效果,因此我们先去修改一下从库的数据。我们把1号的名字由英文改为中文
然后我们去 UserServiceImpl
类中切换数据源。因为 @DS 注解遵循局部优先原则,那么findAll()
一定会到从数据源取数据
@Service
@Transactional
@DS("master")
public class UserServiceImpl implements UserService{
@Autowired
private UserDao userDao;
@Override
@DS("slave_1")
public List<User> findAll() {
return userDao.selectList(null);
}
@Override
public void insert(User user) {
userDao.insert(user);
}
}
再次运行测试类可以发现,findAll()
方法会到从库中去取数据
我们接着在测试类编写插入的方法
@Test
public void testInsert(){
User user = new User();
user.setName("Nomun").setAge(22).setBir(new Date());
userService.insert(user);
}
运行该方法可以看到,主库中已经增加了这条记录
因为我们并没有配置主从同步,所以从库中没有插入这条记录
MyBatis-Plus 官方文档
Mybatis-Plus整合SpringBoot实战教程,提高的你开发效率,后端人员必备!
lombok @Accessors用法