文章目录
mybatis-plus使用
一、依赖引入
pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.4</version>
<relativePath/>
</parent>
<artifactId>mybatis-plus-demo</artifactId>
<dependencies>
<!-- 实现对数据库连接池的自动化配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency> <!-- 本示例,我们使用 MySQL -->
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version>
</dependency>
<!-- 实现对 MyBatis Plus 的自动化配置 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
二、添加相关配置项
application.yaml
spring:
# datasource 数据源配置内容
datasource:
url: jdbc:mysql://127.0.0.1:3306/test-user?useSSL=false&useUnicode=true&characterEncoding=UTF-8
driver-class-name: com.mysql.jdbc.Driver
username: root
password: root1234
三、功能详解
1.自增主键
CREATE TABLE auto_increment (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键',
name VARCHAR(100) NOT NULL COMMENT '名称',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'
) COMMENT='主键自增表';
表单说明:在数据库软件如navicat中直接插入数据时,数据呈现主键自增情况,但使用mybatis-plus直接插入数据则不会。
(1)直接为实体id字段添加注解@TableId(type = IdType.AUTO)
,加上此注解后插入的数据就可以实现主键自增了。
实体类如下:
@TableName(value = "auto_increment")
@Data
public class AutoIncrementDO {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private LocalDateTime createTime;
}
(2)全局配置,在配置文件中加入以下配置
mybatis-plus:
global-config:
db-config:
id-type: auto # 全局主键策略
实体类如下:
@TableName(value = "auto_increment")
@Data
public class AutoIncrementDO {
// 字段名id时会自动识别为主键,否则需要加上 @TableId 注解
private Long id;
private String name;
private LocalDateTime createTime;
}
ps:数据库主键必须设置为自增,否则会报错
2.逻辑删除
CREATE TABLE logic_delete (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键',
name VARCHAR(50) NOT NULL COMMENT '名称',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
deleted BIT NOT NULL DEFAULT FALSE COMMENT '是否删除'
) COMMENT='逻辑删除表';
表单说明:deleted字段默认设为false即0,删除数据时值需要将deleted字段置为1即可,不需要真正的删除数据。
mybatis-plus如何实现这一需求呢?
(1)直接在表示逻辑删除的字段上添加注解@TableLogic
即可,默认值为0,删除值为1,若想反过来需要自定义@TableLogic(value = "1", delval = "0")
或者@TableLogic(value = "false", delval = "true")
(2)全局配置:表示删除的字段上添加注解@TableLogic
,全局配置默认值和删除值,配置如下。
mybatis-plus:
global-config:
db-config:
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
ps:若字段上的注解不仅仅为@TableLogic,而是例如@TableLogic(value = "1", delval = "0")等情况,那么该表的逻辑删除按该注解来,即该表0表示删除
3.操作时间自动填充
CREATE TABLE auto_update_time (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键',
content VARCHAR(255) NOT NULL COMMENT '名称',
create_time TIMESTAMP DEFAULT NULL COMMENT '创建时间',
update_time TIMESTAMP DEFAULT NULL COMMENT '更新时间'
)COMMENT='时间自动更新表';
表单说明:为不产生影响,数据库中创建时间与更新时间不设置默认值,也不自动更新。
mybatis-plus实现操作时间自动填充以及更新步骤如下:
①字段上添加如下注解:
@Data
@TableName("auto_update_time")
public class AutoUpdateTimeDO {
@TableId(type = IdType.AUTO)
private Long id;
private String content;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
②实现 MetaObjectHandler 接口:
@Component
public class DefaultDBFieldHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
setInsertFieldValByName("createTime", LocalDateTime.now(), metaObject);
setInsertFieldValByName("updateTime", LocalDateTime.now(), metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
setUpdateFieldValByName("updateTime", LocalDateTime.now(), metaObject);
}
}
4.其他字段自动填充
如创建人与更新人自动填充,这里会和创建时间以及更新时间自动填充一起,二者实现都需要实现MetaObjectHandler接口。
实现如下:
①字段上添加如下注解:
@Data
@TableName(value = "auto_fill_operator")
public class AutoFillOperatorDO extends BaseDO{
@TableId(type = IdType.AUTO)
private Long id;
private String content;
}
@Data
public class BaseDO {
@TableField(fill = FieldFill.INSERT)
private String creator;
@TableField(fill = FieldFill.INSERT_UPDATE)
private String updater;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
②实现 MetaObjectHandler 接口:
@Component
public class DefaultDBFieldHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
if (metaObject.getOriginalObject() instanceof BaseDO) {
BaseDO baseDO = (BaseDO) metaObject.getOriginalObject();
LocalDateTime current = LocalDateTime.now();
// 创建时间为空,则填充当前时间
if (Objects.isNull(baseDO.getCreateTime())) {
baseDO.setCreateTime(current);
}
// 更新时间为空,则填充当前时间
if (Objects.isNull(baseDO.getUpdateTime())) {
baseDO.setUpdateTime(current);
}
String user = "creator";
// 创建人为空,则填充当前用户
if (Objects.isNull(baseDO.getCreator())) {
baseDO.setCreator(user);
}
// 更新人为空,则填充当前用户
if (Objects.isNull(baseDO.getUpdater())) {
baseDO.setUpdater(user);
}
}
}
@Override
public void updateFill(MetaObject metaObject) {
// 更新时间为空,则以当前时间为更新时间
Object modifyTime = getFieldValByName("updateTime", metaObject);
if (Objects.isNull(modifyTime)) {
setFieldValByName("updateTime", LocalDateTime.now(), metaObject);
}
// 当前登录用户不为空,更新人为空,则当前登录用户为更新人
Object modifier = getFieldValByName("updater", metaObject);
String user = "updater";
if (Objects.isNull(modifier)) {
setFieldValByName("updater", user, metaObject);
}
}
}
5.分页查询
@Mapper
public interface CrudBaseMaoper extends BaseMapper<CrudBaseDO> {
}
@Service
public class CrudBaseServiceImpl {
@Resource
private CrudBaseMaoper crudBaseMaoper;
public IPage<CrudBaseDO> page(String name) {
// 构造分页参数
Page<CrudBaseDO> page = new Page<>(1, 10); // 当前页, 每页显示条数
QueryWrapper<CrudBaseDO> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().like(CrudBaseDO::getCompanyName, name);
return crudBaseMaoper.selectPage(page, queryWrapper);
}
分页查询total为0,需要配置分页插件,如下:
@Configuration
public class MybatisPlusConfig {
// 分页查询拦截器,可以获取total值
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 根据数据库类型选择
return interceptor;
}
}
6.自定义动态查询
场景:在数据库查询中,经常遇到需要根据多个可选字段进行动态查询的情况。传统写法需要为每个字段编写判空逻辑,代码冗长且重复。
通过自定义 LambdaQueryWrapper 扩展方法,实现简洁的动态查询:
示例:
public class LambdaQueryWrapperX<T> extends LambdaQueryWrapper<T> {
public LambdaQueryWrapperX<T> likeIfPresent(SFunction<T, ?> column, String val) {
if (StringUtils.hasText(val)) {
return (LambdaQueryWrapperX<T>) super.like(column, val);
}
return this;
}
}
@Mapper
public interface CrudProMapper extends BaseMapper<CrudProDO> {
}
@Service
public class CrudProServiceImpl implements CrudProService {
@Resource
private CrudProMapper crudProMapper;
@Override
public IPage<CrudProDO> getCrudProPage(CrudProPageReqVO reqVO) {
IPage<CrudProDO> page = new Page<>(1, 10);
return crudProMapper.selectPage(page, new LambdaQueryWrapperX<CrudProDO>()
.likeIfPresent(CrudProDO::getCompanyName, reqVO.getCompanyName())
.likeIfPresent(CrudProDO::getCompanyCode, reqVO.getCompanyCode())
);
}
}
其他方法也可以一样的实现,比如eqIfPresent
7.代码生成器
(1)相关依赖
<!-- 实现对 MyBatis Plus 的自动化配置 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<!-- 代码自动生成器-->
<!-- 与mybatis-plus版本保持一致-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.3.1</version>
</dependency>
<!-- 模板引擎依赖,默认使用 Velocity -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
(2)生成器代码
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.Collections;
public class CodeGenerator {
public static void main(String[] args) {
// 多模块地址前缀,没有可不写
String prefixPath = "/kaishu-sql-crud/kaishu-sql-crud-mybatis-plus-pro/";
FastAutoGenerator.create("jdbc:mysql://127.0.0.1:3306/test-user?useSSL=false&useUnicode=true&characterEncoding=UTF-8",
"root",
"root1234")
.globalConfig(builder -> {
builder.author("kaishu") // 设置作者
.outputDir(System.getProperty("user.dir") + prefixPath + "/src/main/java") // 输出目录
.enableSwagger() // 开启swagger
.fileOverride(); // 覆盖已生成文件
})
.packageConfig(builder -> {
builder.parent("org.kaishu.sql.crud.mybatis.plus.pro") // 设置父包名
.moduleName("generatordemo") // 设置父包模块名
.pathInfo(Collections.singletonMap(OutputFile.xml,
System.getProperty("user.dir") + prefixPath + "/src/main/resources/mapper")); // 设置mapperXml路径
})
.strategyConfig(builder -> {
builder.addInclude("t_auto_genarater", "t_test_auto_genarater") // 设置需要生成的表名
.addTablePrefix("t_", "t_") // 设置过滤表前缀
.entityBuilder() // 实体类配置
.enableLombok() // 启用Lombok
.controllerBuilder() // Controller配置
.enableRestStyle() // 启用@RestController
.mapperBuilder() // Mapper配置
.enableMapperAnnotation(); // 启用@Mapper
})
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎
.execute();
}
}
更详细的配置参考如下:
(1)实体类生成配置
.strategyConfig(builder -> {
builder.entityBuilder()
.enableLombok() // 使用Lombok
.enableChainModel() // 链式模型
.enableRemoveIsPrefix() // 移除is前缀
.enableTableFieldAnnotation() // 字段注解
.versionColumnName("version") // 乐观锁字段名
.logicDeleteColumnName("deleted") // 逻辑删除字段名
.naming(NamingStrategy.underline_to_camel) // 命名策略
.columnNaming(NamingStrategy.underline_to_camel)
.addSuperEntityColumns("id", "create_time", "update_time") // 公共字段
.formatFileName("%sEntity") // 文件名称格式
.addIgnoreColumns("is_deleted"); // 忽略字段
})
(2)Mapper生成配置
.strategyConfig(builder -> {
builder.mapperBuilder()
.enableBaseResultMap() // 生成resultMap
.enableBaseColumnList() // 生成columnList
.enableMapperAnnotation() // @Mapper注解
.formatMapperFileName("%sDao") // Mapper文件命名
.formatXmlFileName("%sXml"); // Xml文件命名
})
(3)Service生成配置
.strategyConfig(builder -> {
builder.serviceBuilder()
.formatServiceFileName("%sService") // Service接口命名
.formatServiceImplFileName("%sServiceImpl"); // Service实现类命名
})
(4)Controller生成配置
.strategyConfig(builder -> {
builder.controllerBuilder()
.enableRestStyle() // 使用@RestController
.enableHyphenStyle() // 使用驼峰转连字符
.formatFileName("%sController") // 文件命名
.enableFileOverride(); // 覆盖已生成文件
})
8.代码生成器(自定义模板)
示例:这里只自定义实体类生成模板,其余照旧,这里我需要让实体类都继承我已定义好的的类BaseDO
(1)依赖同上
(2)生成器代码,基本同上
.strategyConfig(builder -> {
...
})
// 加上这行代码即可
.templateConfig(builder -> {
builder.entity("/templates/custom-entity.java");
})
(2)自定义实体模板文件,在\src\main\resources\templates\
目录下新建custom-entity.java.ftl
文件,文件内容如下
package ${package.Entity};
<#-- 基础导入 -->
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import lombok.EqualsAndHashCode;
<#-- 导入项目基础类 -->
import org.kaishu.sql.crud.mybatis.plus.pro.dal.dataobject.BaseDO;
<#-- 类注释 -->
/**
* ${table.comment!} 实体类
*
* @author ${author}
* @since ${date}
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("${table.name}")
public class ${entity} extends BaseDO {
private static final long serialVersionUID = 1L;
<#-- 生成字段(自动排除BaseDO中已存在的字段) -->
<#list table.fields as field>
<#if !['id','create_time','update_time','creator','updater','deleted']?seq_contains(field.name)>
/**
* ${field.comment!}
*/
@TableField("${field.name}")
private ${field.propertyType} ${field.propertyName};
</#if>
</#list>
}
·