【Spring Boot后端组件】mybatis-plus使用
文章目录
- mybatis-plus使用
- 一、依赖引入
- 二、添加相关配置项
- 三、功能详解
- 1.自增主键
- 2.逻辑删除
- 3.操作时间自动填充
- 4.其他字段自动填充
- 5.分页查询
- 6.自定义动态查询
- 7.代码生成器
- 8.代码生成器(自定义模板)
mybatis-plus使用
一、依赖引入
pom.xml文件
4.0.0 org.springframework.boot >spring-boot-starter-parent 2.7.4 >mybatis-plus-demo org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 com.baomidou mybatis-plus-boot-starter 3.2.0 org.springframework.boot spring-boot-starter-web org.projectlombok lombok
二、添加相关配置项
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 { }
@Service public class CrudBaseServiceImpl { @Resource private CrudBaseMaoper crudBaseMaoper; public IPage page(String name) { // 构造分页参数 Page page = new Page(1, 10); // 当前页, 每页显示条数 QueryWrapper 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 extends LambdaQueryWrapper { public LambdaQueryWrapperX likeIfPresent(SFunction column, String val) { if (StringUtils.hasText(val)) { return (LambdaQueryWrapperX) super.like(column, val); } return this; } }
@Mapper public interface CrudProMapper extends BaseMapper { }
@Service public class CrudProServiceImpl implements CrudProService { @Resource private CrudProMapper crudProMapper; @Override public IPage getCrudProPage(CrudProPageReqVO reqVO) { IPage page = new Page(1, 10); return crudProMapper.selectPage(page, new LambdaQueryWrapperX() .likeIfPresent(CrudProDO::getCompanyName, reqVO.getCompanyName()) .likeIfPresent(CrudProDO::getCompanyCode, reqVO.getCompanyCode()) ); } }
其他方法也可以一样的实现,比如eqIfPresent
7.代码生成器
(1)相关依赖
com.baomidou mybatis-plus-boot-starter 3.5.3.1 com.baomidou mybatis-plus-generator 3.5.3.1 org.apache.velocity velocity-engine-core 2.3 org.freemarker freemarker
(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; /** * ${field.comment!} */ @TableField("${field.name}") private ${field.propertyType} ${field.propertyName}; }
·