一、概述
1.MyBatis Plus 是什么?
MyBatis Plus(简称 MP)是一个基于 MyBatis 的增强工具,它简化了 MyBatis 的开发流程,提供了更强大、更便捷的功能,使得开发人员能够更加高效地进行数据库操作和开发。MyBatis Plus 提供了一系列的注解和方法,可以帮助开发人员快速构建和执行 SQL 语句,减少了手写 SQL 的工作量。
2.MyBatis Plus 的特点和优势
- 简化开发:MyBatis Plus 提供了一系列的注解和方法,可以帮助开发人员快速构建和执行 SQL 语句,减少了手写 SQL 的工作量,提高了开发效率。
- 强大的 CRUD 功能:MyBatis Plus 提供了一套强大的 CRUD 方法,可以直接通过调用方法来完成数据库的增删改查操作,无需手动编写 SQL 语句。
- 支持分页查询:MyBatis Plus 内置了分页查询的功能,可以方便地进行分页查询操作,减少了开发人员的工作量。
- 支持多表关联查询:MyBatis Plus 提供了一系列的方法和注解,可以方便地进行多表关联查询,简化了复杂查询的编写过程。
- 支持代码生成器:MyBatis Plus 提供了一个代码生成器,可以根据数据库表结构自动生成实体类、Mapper 接口和 XML 文件,减少了手动编写代码的工作量。
- 支持乐观锁和逻辑删除:MyBatis Plus 内置了乐观锁和逻辑删除的功能,可以方便地实现数据的并发控制和逻辑删除操作。
- 支持缓存管理:MyBatis Plus 提供了缓存管理的功能,可以方便地配置和使用缓存,提高了系统的性能和响应速度。
3.MyBatis Plus 的适用场景
- 中小型项目:MyBatis Plus 简化了 MyBatis 的开发流程,提供了更强大、更便捷的功能,适用于中小型项目的开发。
- 数据库操作频繁的项目:MyBatis Plus 提供了强大的 CRUD 功能和分页查询功能,适用于数据库操作频繁的项目。
- 多表关联查询较多的项目:MyBatis Plus 提供了多表关联查询的功能,可以方便地进行多表关联查询,适用于多表关联查询较多的项目。
- 需要快速开发的项目:MyBatis Plus 提供了代码生成器的功能,可以根据数据库表结构自动生成代码,适用于需要快速开发的项目。
- 需要乐观锁和逻辑删除功能的项目:MyBatis Plus 内置了乐观锁和逻辑删除的功能,适用于需要进行数据并发控制和逻辑删除的项目。 总之,MyBatis Plus 是一个功能强大、易于使用的数据库操作工具,适用于各种类型的项目开发。
二、快速入门
2.1初始化工程
创建一个空的 Spring Boot 工程
这里我们直接跳过
2.2添加依赖
引入 Spring Boot Starter 父工程:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5+ 版本</version>
<relativePath/>
</parent>
引入 spring-boot-starter
、spring-boot-starter-test
、mybatis-plus-boot-starter
、mysql
lombok
依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
2.3配置
在application.yml配置文件中添加mysql数据库的相关配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/testdb?serverTimezone=GMT%2B8&useSSL=false
username: root
password: 123456
在 Spring Boot 启动类中添加 @MapperScan
注解,扫描 Mapper 文件夹:
@SpringBootApplication
//根据自己的项目路径修改
@MapperScan("com.cqgcxy.mapper")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
2.4编码
编写实体类 User
@Data
@TableName("`user`")//指定数据库的user表
public class User {
@TableId(value = "uid", type = IdType.AUTO)
private Integer uid;
@TableField("userName")
private String username;
@TableField("userPassword")
private String userpassword;
private Integer sex;
private String hobbit;
private String degree;
private String note;
private Integer rule;
private Integer state;
}
编写 Mapper 包下的 UserMapper
接口
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
2.5开始使用
MybatisPlus提供了很多模板供我们使用,我们在这里对部分模板进行学习, 选择测试类,进行功能测试:
UserMapper 中的 selectList()
方法的参数为 MP 内置的条件封装器 Wrapper
,所以不填写就是无任何条件
/**
* 查询user表全部数据
*/
@Test
void selectUserAll() {
List<User> userList = userMapper.selectList(null);
userList.forEach(System.out::println);
}
控制台输出
User(uid=1, username=aa, userpassword=123456, sex=0, hobbit=打游戏, degree=本科, note=张三, rule=0, state=1)
User(uid=2, username=bb, userpassword=123456, sex=1, hobbit=唱, degree=博士, note=李四, rule=0, state=1)
User(uid=3, username=cc, userpassword=123456, sex=0, hobbit=跳, degree=硕士, note=王五, rule=1, state=1)
User(uid=4, username=哈哈, userpassword=123456, sex=1, hobbit=rap, degree=本科, note=老六, rule=0, state=1)
User(uid=13, username=小小, userpassword=123456, sex=null, hobbit=null, degree=null, note=null, rule=null, state=null)
User(uid=14, username=大大, userpassword=123456, sex=null, hobbit=null, degree=null, note=null, rule=null, state=null)
User(uid=15, username=大明, userpassword=123456, sex=null, hobbit=null, degree=null, note=null, rule=null, state=null)
User(uid=16, username=小a, userpassword=987654, sex=null, hobbit=null, degree=null, note=null, rule=null, state=null)
三、实体类的常用注解配置
1.@TableName 注解
用于指定实体类对应的数据库表名。可以在实体类上使用该注解,通过value属性指定表名。
2.@TableField 注解
用于指定实体类中字段与数据库表中字段的映射关系。可以在实体类的属性上使用该注解,通过value属性指定字段名。
3.@TableId 注解
用于指定实体类中的主键字段。可以在实体类的属性上使用该注解,通过value属性指定主键字段名。
4.@Version 注解
用于实现乐观锁机制。可以在实体类的属性上使用该注解,表示该字段用于记录数据版本号 。
5.@TableLogic 注解
用于实现逻辑删除功能。可以在实体类的属性上使用该注解,表示该字段用于标记逻辑删除状态。
6.@TableIndex 注解
用于指定数据库表的索引。可以在实体类上使用该注解,通过value属性指定索引名,通过columns属性指定索引的字段。
四、掌握CRUD操作和条件构造器的使用
1.什么是CRUD?基本的 CRUD 的使用
CRUD是指对数据进行增加(Create)、查询(Retrieve)、更新(Update)和删除(Delete)等操作的一种常见的数据操作方式。
- Create:创建数据,即向数据库中插入新的记录。
- Retrieve:查询数据,即从数据库中获取符合条件的记录。
- Update:更新数据,即修改数据库中已有的记录。
- Delete:删除数据,即从数据库中删除指定的记录
而通过使用MyBatis-Plus的模板相比mybatis,可以简化CRUD操作的编写,提高开发效率,
新增:
/**
* 新增
* @param user
* @return
*/
@PostMapping("save")
public Boolean add(@RequestBody User user){
return userService.save(user);
}
删除
/**
* 删除(根据id)
* @param id
* @return
*/
@DeleteMapping("delete")
public Boolean delete(int id){
return userService.removeById(id);
}
修改(如果数据存在则为修改,如果数据不存在则为新增)
/**
* 新增或修改(根据id)
* @param user
* @return
*/
@PostMapping("saveOrUpdate")
public Boolean update(@RequestBody User user){
return userService.saveOrUpdate(user);
}
查询
/**
* 查询
* @return
*/
@GetMapping("list")
public List<User> list(){
return userService.list();
}
2.条件构造器
1.QueryWrapper
使用QueryWrapper构建查询条件,通过eq方法添加年龄等于20的条件,通过like方法添加姓名包含"Tom"的条件,通过orderByDesc方法按照创建时间降序排序,然后使用selectList方法执行查询操作。
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("age", 20)
.like("name", "Tom")
.orderByDesc("create_time");
List<User> userList = userMapper.selectList(queryWrapper);
2.LambdaQueryWrapper
使用LambdaQueryWrapper构建查询条件,通过eq方法使用Lambda表达式添加年龄等于20的条件,通过like方法使用Lambda表达式添加姓名包含"Tom"的条件,通过orderByDesc方法使用Lambda表达式按照创建时间降序排序,然后使用selectList方法执行查询操作。
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(User::getAge, 20)
.like(User::getName, "Tom")
.orderByDesc(User::getCreateTime);
List<User> userList = userMapper.selectList(lambdaQueryWrapper);
3.UpdateWrapper
使用UpdateWrapper构建更新条件,通过set方法设置年龄为25,通过eq方法添加姓名等于"Tom"的条件,然后使用update方法执行更新操作。
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.set("age", 25)
.eq("name", "Tom");
int rows = userMapper.update(null, updateWrapper);
4.LambdaUpdateWrapper
使用LambdaUpdateWrapper构建更新条件,通过set方法使用Lambda表达式设置年龄为25,通过eq方法使用Lambda表达式添加姓名等于"Tom"的条件,然后使用update方法执行更新操作
LambdaUpdateWrapper<User> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
lambdaUpdateWrapper.set(User::getAge, 25)
.eq(User::getName, "Tom");
int rows = userMapper.update(null, lambdaUpdateWrapper);
2.分页模糊查询的实现
/**
* 分页模糊查询
* @param query
* @return
*/
@PostMapping("listPage")
public List<User> liseUserPage(@RequestBody PageParam query){
HashMap param = query.getParam();
String name= (String)param.get("username");
// 创建分页对象,设置当前页码和每页显示数量
Page<User> page = new Page();
page.setCurrent(query.getPageNum());
page.setSize(query.getPageSize());
// 创建查询条件,使用Lambda表达式实现模糊查询
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper();
lambdaQueryWrapper.like(User::getUsername,name);
// 执行分页查询,返回查询结果
IPage result = userService.page(page,lambdaQueryWrapper);
// 返回查询结果列表
return result.getRecords();
}
3.自定义 SQL 的执行
这里用分页模糊查询作为例子
controller层
/**
* 自定义分页模糊查询
* @param query
* @return
*/
@PostMapping("listPageCC")
public List<User> liseUserPageCC(@RequestBody PageParam query){
HashMap param = query.getParam();
String name= (String)param.get("username");
// 创建分页对象,设置当前页码和每页显示数量
Page<User> page = new Page();
page.setCurrent(query.getPageNum());
page.setSize(query.getPageSize());
// 创建分页对象,设置当前页码和每页显示数量
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper();
lambdaQueryWrapper.like(User::getUsername,name);
// 执行自定义分页查询,返回查询结果
IPage result = userService.pageCC(page,lambdaQueryWrapper);
// 返回查询结果列表
return result.getRecords();
}
service层
小扩展:
Service是一种设计模式,用于将业务逻辑与数据访问逻辑分离。Service层负责处理业务逻辑,包括数据的处理、转换、验证等。它与具体的数据访问方式(如数据库、文件等)无关,使得业务逻辑更加独立、可复用和可测试。
Serviceimpl(Service接口)定义了业务逻辑的方法,而Service实现类则实现了这些方法,具体实现了业务逻辑的处理。
为什么要这么做呢?
- 分离关注点:将业务逻辑与数据访问逻辑分离,使得代码更加清晰、可维护和可测试。
- 提高复用性:将业务逻辑封装在Service中,可以在不同的场景下复用。
- 降低耦合性:Service层与具体的数据访问方式解耦,使得业务逻辑不依赖于具体的数据访问实现。
- 提供事务管理:Service层可以管理事务,确保业务操作的一致性和完整性。
service
public interface UserService extends IService<User> {
//自定义分页模糊查询
IPage pageCC(IPage<User> page, Wrapper wrapper);
}
serviceimpl
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Resource
private UserMapper userMapper;
//自定义分页模糊查询
@Override
public IPage pageCC(IPage<User> page, Wrapper wrapper) {
return userMapper.pageCC(page,wrapper);
}
}
mapper层
@Mapper
public interface UserMapper extends BaseMapper<User> {
//自定义分页模糊查询
IPage pageCC(IPage<User> page,@Param(Constants.WRAPPER) Wrapper wrapper);//爆红不影响
}
sql语句
${ew.customSqlSegment}是MyBatis Plus框架中的一个占位符,用于动态生成SQL语句的条件部分。在MyBatis Plus中,使用Wrapper对象来构建查询条件,ew就是Wrapper对象的实例。
<?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.cqgcxy.mapper.UserMapper">
<!-- 自定义分页模糊查询-->
<select id="pageCC" resultType="com.cqgcxy.pojo.User">
select * from user ${ew.customSqlSegment}
</select>
</mapper>
五、代码生成器的使用
5.1 导入依赖
<!-- mybatis-plus支持-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<!-- 代码自动生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.30</version>
</dependency>
<dependency>
<groupId>com.spring4all</groupId>
<artifactId>spring-boot-starter-swagger</artifactId>
<version>1.5.1.RELEASE</version>
</dependency>
5.2 创建代码生成器
在java/com/cqgcxy/common路径下创建CodeGenerator类
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class CodeGenerator {
/**
* <p>
* 读取控制台内容
* </p>
*/
public static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
StringBuilder help = new StringBuilder();
help.append("请输⼊" + tip + ":");
System.out.println(help.toString());
if (scanner.hasNext()) {
String ipt = scanner.next();
if (StringUtils.isNotBlank(ipt)) {
return ipt;
}
}
throw new MybatisPlusException("请输⼊正确的" + tip + "!");
}
/**
* 操作步骤:
* 1.修改数据源包括地址密码信息,对应代码标记:⼀、 下同
* 2.模块配置,可以修改包名
* 3.修改模板(这步可忽略)
* @param args
*/
public static void main(String[] args) {
// 代码⽣成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("Jocker");
gc.setOpen(false);
gc.setSwagger2(true); //实体属性 Swagger2 注解
gc.setBaseResultMap(true);// XML ResultMap
gc.setBaseColumnList(true);// XML columList
//去掉service接⼝⾸字⺟的I, 如DO为User则叫UserService
gc.setServiceName("%sService");
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
// ⼀、修改数据源
dsc.setUrl("jdbc:mysql://localhost:3306/testdb?useUnicode=true&characterEncoding=UTF8&useSSL=false");
// dsc.setSchemaName("public");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
//pc.setModuleName(scanner("模块名"));
// ⼆、模块配置
pc.setParent("com.cqgcxy")
.setEntity("pojo")
.setMapper("mapper")
.setService("service")
.setServiceImpl("service.impl")
.setController("controller");
mpg.setPackageInfo(pc);
// ⾃定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// to do nothing
}
};
// 如果模板引擎是 freemarker
String templatePath = "templates/mapper.xml.ftl";
// 如果模板引擎是 velocity
// String templatePath = "/templates/mapper.xml.vm";
// ⾃定义输出配置
List<FileOutConfig> focList = new ArrayList<>();
// ⾃定义配置会被优先输出
focList.add(new FileOutConfig(templatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// ⾃定义输出⽂件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发⽣变化!!
return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
+ "/" + tableInfo.getEntityName() + "Mapper" +
StringPool.DOT_XML;
}
});
/*
cfg.setFileCreate(new IFileCreate() {
@Override
public boolean isCreate(ConfigBuilder configBuilder, FileType fileType,
String filePath) {
// 判断⾃定义⽂件夹是否需要创建
checkDir("调⽤默认⽅法创建的⽬录,⾃定义⽬录⽤");
if (fileType == FileType.MAPPER) {
// 已经⽣成 mapper ⽂件判断存在,不想重新⽣成返回 false
return !new File(filePath).exists();
}
// 允许⽣成模板⽂件
return true;
}
});
*/
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// 3. ⽣成代码并测试
// 四、实现增删改查
// 配置模板
TemplateConfig templateConfig = new TemplateConfig();
// 配置⾃定义输出模板
//指定⾃定义模板路径,注意不要带上.ftl/.vm, 会根据使⽤的模板引擎⾃动识别
// 三、修改模板
/*templateConfig.setEntity("templates/entity2.java");
templateConfig.setService("templates/service2.java");
templateConfig.setController("templates/controller2.java");
templateConfig.setMapper("templates/mapper2.java");
templateConfig.setServiceImpl("templates/serviceimpl2.java");*/
templateConfig.setXml(null);
mpg.setTemplate(templateConfig);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
// strategy.setSuperEntityClass("你⾃⼰的⽗类实体,没有就不⽤设置!");
//strategy.setSuperEntityClass("BaseEntity");
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
// 公共⽗类
//strategy.setSuperControllerClass("BaseController");
// strategy.setSuperControllerClass("你⾃⼰的⽗类控制器,没有就不⽤设置!");
// 写于⽗类中的公共字段
// strategy.setSuperEntityColumns("id");
strategy.setInclude(scanner("表名,多个英⽂逗号分割").split(","));
strategy.setControllerMappingHyphenStyle(true);
//strategy.setTablePrefix(pc.getModuleName() + "_");
// 忽略表前缀tb_,⽐如说tb_user,直接映射成user对象
// 四、注意是否要去掉表前缀
//strategy.setTablePrefix("tb_");
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
mpg.execute();
}
}
5.3 需修改CodeGenerator的部分代码:
建立对应的包名
修改数据数据库配置
5.4运行CodeGenerator类
输入数据库存在的表名
运行成功,代码生成成功
注意添加Mapper注解
代码生成器没有添加Mapper注解,或者在启动类添加@MapperScan扫描包二选一
六、MyBatis Plus的优缺点
优点
- 简化开发:MyBatis Plus提供了很多便捷的API和工具,可以大大简化数据库操作的开发工作。例如,通过继承BaseMapper接口,可以直接使用常用的增删改查方法,无需手动编写SQL语句。
- 提高效率:MyBatis Plus提供了代码生成器,可以根据数据库表结构自动生成实体类、Mapper接口和XML映射文件,减少了手动编写重复代码的工作量,提高了开发效率。
- 强大的查询功能:MyBatis Plus提供了丰富的查询方法和条件构造器,可以灵活地进行复杂的查询操作。例如,可以通过Lambda表达式来构建查询条件,使得查询语句更加简洁和易读。
- 支持分页查询:MyBatis Plus内置了分页插件,可以方便地进行分页查询操作,减少了手动编写分页逻辑的工作量。
- 支持多种数据库:MyBatis Plus支持多种主流的关系型数据库,如MySQL、Oracle、SQL Server等,可以在不同的数据库之间无缝切换。
不足
- 灵活性受限:MyBatis Plus提供了很多便捷的功能和API,但有时候可能会受到框架的限制,无法满足一些特定的需求。在某些复杂的场景下,可能需要手动编写SQL语句或者自定义插件来实现。
- 过度封装:MyBatis Plus对MyBatis进行了封装和扩展,提供了更高层次的抽象和封装,但有时候可能会导致开发者对底层的SQL执行细节不够了解,难以进行性能优化和调优。