Appearance
Mybatis Plus扩展方法
1、环境准备
- JDK17
1、SQL脚本
mysql
create table grade
(
id varchar(32) comment '主键' primary key,
grade_name varchar(100) null comment '班级名称',
del_flag tinyint(1) default 0 null comment '删除状态(0-正常,1-已删除)',
create_by varchar(255) null comment '创建人',
create_time datetime null comment '创建时间',
update_by varchar(255) null comment '修改人',
update_time datetime null comment '修改时间'
)comment '班级表';
INSERT INTO grade (id, grade_name, del_flag, create_by, create_time, update_by, update_time) VALUES ('1', '高三一班', 0, '1', now(), '1', now());
2、公共实体类
java
package com.xx.entity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* @Author: xueqimiao
* @Date: 2023/11/1 13:38
*/
@Data
public class BaseModel implements Serializable {
@Schema(description = "创建人")
private String createBy;
@Schema(description = "创建时间")
private Date createTime;
@Schema(description = "更新人")
private String updateBy;
@Schema(description = "更新时间")
private Date updateTime;
@Schema(description = "0 正常,1已删除")
private Integer delFlag;
}
3、实体类
java
package com.xx.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* @Author: xueqimiao
* @Date: 2023/11/1 13:42
*/
@Data
@TableName("grade")
public class GradeModel extends BaseModel{
@Schema(description = "主键id")
private String id;
@Schema(description = "班级名称")
private String gradeName;
}
4、Mapper层
xml
<?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.xx.mapper.GradeMapper">
</mapper>
java
package com.xx.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xx.entity.GradeModel;
/**
* @Author: xueqimiao
* @Date: 2023/11/1 13:46
*/
public interface GradeMapper extends BaseMapper<GradeModel> {
}
5、Service层
java
package com.xx.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xx.entity.GradeModel;
/**
* @Author: xueqimiao
* @Date: 2023/11/1 13:45
*/
public interface GradeService extends IService<GradeModel> {
}
java
package com.xx.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xx.entity.GradeModel;
import com.xx.mapper.GradeMapper;
import com.xx.service.GradeService;
import org.springframework.stereotype.Service;
/**
* @Author: xueqimiao
* @Date: 2023/11/1 13:45
*/
@Service
public class GradeServiceImpl extends ServiceImpl<GradeMapper, GradeModel> implements GradeService {
}
6、Controller层
java
package com.xx.controller;
import com.xx.common.ResultData;
import com.xx.entity.GradeModel;
import com.xx.service.GradeService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @Author: xueqimiao
* @Date: 2023/11/1 13:47
*/
@RestController
@Slf4j
@Tag(name = "GradeController", description = "班级控制层")
public class GradeController {
@Resource
private GradeService gradeService;
@GetMapping("/getList")
@Operation(summary = "班级列表")
public ResultData getList(){
List<GradeModel> gradeList = gradeService.list();
return new ResultData(gradeList);
}
}
7、MybatisPlus相关配置
yaml
# mybatis-plus相关配置
mybatis-plus:
# xml扫描,多个目录用逗号或者分号分隔(告诉 Mapper 所对应的 XML 文件位置)
mapper-locations: classpath:mapper/*Mapper.xml
# type-enums-package: com.xx.enums
configuration:
# 是否开启自动驼峰命名规则映射:从数据库列名到Java属性驼峰命名的类似映射
map-underscore-to-camel-case: true
# 如果查询结果中包含空值的列,则 MyBatis 在映射的时候,不会映射这个字段
call-setters-on-nulls: true
# 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
cache-enabled: true
db-config:
logic-delete-field: del_flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
#Mybatis输出sql日志
logging:
level:
com.xx.mapper: info
8、POM文件
xml
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3</version>
</dependency>
2、逻辑删除
1、逻辑删除和物理删除
物理删除:指文件存储所用到的磁存储区域被真正的擦除或清零,这样删除的文件是不可以恢复的,物理删除是计算机处理数据时的一个概念。如果在数据库中直接使用delete、drop删除了表数据,如果没有备份的话,数据就很难恢复了。
逻辑删除(软删除):逻辑删除就是对要被删除的数据打上一个删除标记,通常使用一个deleted字段标示行记录是不是被删除,比如该数据有一个字段deleted,当其值为0表示未删除,值为1表示删除。那么逻辑删除就是将0变成1。在逻辑上是数据是被删除的,但数据本身是依然存在的。
物理删除一定程度上删除了“无用”的数据,降低了表的数据量,对性能肯定是有好处的;但是如果没有备份的话,数据很难恢复。也无法对历史数据进行数据分析。
逻辑删除恢复的话只要修改deleted等类似的状态标示字段就可以了,但是表的数据量肯定会比物理删除增加了,并且查询时经常要考虑到deleted字段,对索引都会有影响。
所以一张表的数据是否采用逻辑删除,还要根据数据的重要性、数据量、查询性能以及业务需求等因素综合判断。
2、@TableLogic
java
@TableLogic
private Integer delFlag;
3、测试
mysql
==> Preparing: SELECT id,grade_name,create_by,create_time,update_by,update_time,del_flag FROM grade WHERE del_flag=0
==> Parameters:
<== Columns: id, grade_name, create_by, create_time, update_by, update_time, del_flag
<== Row: 1, 高三一班, 1, 2023-11-01 13:44:16, 1, 2023-11-01 13:44:20, 0
<== Total: 1
3、主键生成策略
1、局部和全局设置主键生成策略
1、局部设置
java
@TableId(type = IdType.ASSIGN_ID)
private String id;
2、全局设置
yaml
mybatis-plus:
global-config:
db-config:
id-type: auto
2、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 方法) |
特别说明:
如果设置类型是AUTO自增策略,数据库字段一定设置自增。
在没有进行设置主键生成算法的时候,默认算法是雪花算法。
3、不配置主键策略
java
package com.xx;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
/**
* @Author: xueqimiao
* @Date: 2023/11/1 14:14
*/
@Data
public class GradeDTO implements Serializable {
@Schema(description = "主键id")
private String id;
@Schema(description = "班级名称")
private String gradeName;
}
java
@PostMapping("/saveGrade")
@Operation(summary = "添加班级")
public ResultData saveGrade(@RequestBody GradeDTO grade){
if(ValidationUtil.isEmpty(grade)){
return new ResultData(ResultCodeEnum.DATA_EMPTY_FAIL);
}
gradeService.save(ValueUtil.copyFieldValue(grade,GradeModel.class));
return new ResultData();
}
json
{
"gradeName": "高三二班"
}
java
==> Preparing: INSERT INTO grade ( id, grade_name ) VALUES ( ?, ? )
==> Parameters: 1719599922669457410(String), 高三二班(String)
4、IdType.AUTO
如果设置类型是AUTO自增策略,数据库字段一定设置自增。
5、IdType.NONE
采用默认的
6、IdType.INPUT
需要自己set主键值
java
### SQL: INSERT INTO grade ( id, grade_name ) VALUES ( ?, ? )
### Cause: java.sql.SQLIntegrityConstraintViolationException: Column 'id' cannot be null
; Column 'id' cannot be null] with root cause
7、IdType.ASSIGN_ID
雪花id
8、IdType.ASSIGN_UUID
UUID
java
==> Preparing: INSERT INTO grade ( id, grade_name ) VALUES ( ?, ? )
==> Parameters: daf48291d4274a15e336424f98606a5e(String), 高三二班(String)
4、方法扩展
1、IService
更改为
BaseService
java
package com.xx.mybatis;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xx.utils.ColumnUtil;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
/**
* @Author: xueqimiao
* @Date: 2022/4/7 14:26
*/
public interface BaseService<T> extends IService<T> {
/**
* 检查该字段集合组成的数据是否重复
*
* @param id 更新时所用的主键值,新增时传入空
* @param fields 不能重复的字段集
* @param values 需要检查重复的字段值集
* @return 重复返回true, 不重复返回false
*/
boolean checkDuplicate(String id, String[] fields, String[] values);
/**
* 检查该字段集合组成的数据是否重复
*
* @param id 更新时所用的主键值,新增时传入空
* @param fn 不能重复的字段
* @param value 需要检查重复的字段值
* @return 重复返回true, 不重复返回false
*/
boolean checkDuplicate(String id, ColumnUtil.SFunction<T, ?> fn, String value);
/**
* 根据多个主键查询
* @param ids 主键几个
* @return 对象对象集合
*/
List<T> getByIds(Collection<? extends Serializable> ids);
/**
* 查询list返回 指定key value为T的 map
* @param queryWrapper
* @param key 指定key Function
* @return
*/
<E> Map<E,T> getListToMap(Wrapper<T> queryWrapper, Function<T, E> key);
/**
* 查询list返回 指定key value为T的 map
* @param key 指定key Function
* @return
*/
<E> Map<E,T> getListToMap(Function<T, E> key);
/**
* 查询list返回 指定key value为List<T>的 map
* @param queryWrapper
* @param key 指定key Function
* @return
*/
<E> Map<E,List<T>> getListToMaps(Wrapper<T> queryWrapper, Function<T, E> key);
/**
* 查询list返回 指定key value为List<T>的 map
* @param key 指定key Function
* @return
*/
<E> Map<E,List<T>> getListToMaps(Function<T, E> key);
/**
* 获取一条数据
* @param lambdaQuery
* @return
*/
T getSingle(LambdaQueryWrapper<T> lambdaQuery);
/**
* 获取一条数据
* @param fn
* @param value
* @return
*/
T getSingle(ColumnUtil.SFunction<T, ?> fn, Object value);
/**
* 根据单个字段查询列表
* @param fn
* @param values
* @return
*/
List<T> getList(ColumnUtil.SFunction<T, ?> fn, List values);
/**
* 根据单个字段查询列表
* @param fn
* @param value
* @return
*/
List<T> getList(ColumnUtil.SFunction<T, ?> fn, Object value);
/**
* 获取列表
* @param queryWrapper
* @return
*/
default List<T> getList(Wrapper<T> queryWrapper) {
return this.getBaseMapper().selectList(queryWrapper);
}
/**
* 获取列表
* @return
*/
default List<T> getList() {
return this.list(Wrappers.emptyWrapper());
}
/**
* 根据实体对象查询
* @param entity 传入的实体对象要序列化
* @return
*/
List<T> getByEntity(Serializable entity);
/**
* 根据id进行物理删除,不会走逻辑删除逻辑
* @param id
* @return
*/
int physicalDeleteById(Serializable id);
/**
* 根据id进行物理删除,不会走逻辑删除逻辑
* @param ids
* @return
*/
int physicalDeleteByIds(Collection<? extends Serializable> ids);
/**
* 根据传入的对象进行更新,并在字段为 null 时将数据库中对应字段置空
* @param entity
* @return
*/
boolean updateWithNullFields(T entity);
///**
// * 根据实体对象查询
// * @param source
// * @param clazz
// * @return
// * @param <E>
// */
//<E> List<E> getByEntity(Serializable source,Class<E> clazz);
}
2、ServiceImpl
更改为
BaseServiceImpl
java
package com.xx.mybatis;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xx.utils.ColumnUtil;
import com.xx.utils.FunctionUtils;
import com.xx.utils.ValidationUtil;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
/**
* @Author: xueqimiao
* @Date: 2022/4/7 15:05
*/
public class BaseServiceImpl<M extends BasePlusMapper<T>, T> extends ServiceImpl<M, T> implements BaseService<T> {
/**
* 检查该字段集合组成的数据是否重复
*
* @param id 更新时所用的主键值,新增时传入空
* @param fields 不能重复的字段集
* @param values 需要检查重复的字段值集
* @return 重复返回true, 不重复返回false
*/
@Override
public boolean checkDuplicate(String id, String[] fields, String[] values) {
if (ValidationUtil.isEmpty(fields) || ValidationUtil.isEmpty(values) || fields.length != values.length) {
return true;
}
QueryWrapper<T> wrapper = new QueryWrapper<>();
if (!ValidationUtil.isEmpty(id)) {
TableInfo tableInfo = TableInfoHelper.getTableInfo(this.entityClass);
String keyProperty = tableInfo.getKeyProperty();
wrapper.ne(keyProperty, id);
}
for (int pos = 0; pos < fields.length; pos++) {
wrapper.eq(fields[pos], values[pos]);
}
wrapper.last("limit 1");
Long count = count(wrapper);
return count > 0;
}
/**
* 检查该字段集合组成的数据是否重复
*
* @param id 更新时所用的主键值,新增时传入空
* @param fn 不能重复的字段
* @param value 需要检查重复的字段值
* @return 重复返回true, 不重复返回false
*/
@Override
public boolean checkDuplicate(String id, ColumnUtil.SFunction<T, ?> fn, String value) {
String field = ColumnUtil.getFieldName(fn, "_", 2);
if (ValidationUtil.isEmpty(field) || ValidationUtil.isEmpty(value)) {
return true;
}
QueryWrapper<T> wrapper = new QueryWrapper<>();
if (!ValidationUtil.isEmpty(id)) {
TableInfo tableInfo = TableInfoHelper.getTableInfo(this.entityClass);
String keyProperty = tableInfo.getKeyProperty();
wrapper.ne(keyProperty, id);
}
wrapper.eq(field, value);
Long count = count(wrapper);
return count > 0;
}
/**
* 根据多个主键查询
*
* @param ids 主键几个
* @return 对象对象集合
*/
@Override
public List<T> getByIds(Collection<? extends Serializable> ids) {
if (ValidationUtil.isEmpty(ids)) {
return Collections.emptyList();
}
return listByIds(ids);
}
@Override
public <E> Map<E, T> getListToMap(Wrapper<T> queryWrapper, Function<T, E> key) {
return FunctionUtils.listToMap(list(queryWrapper), key);
}
@Override
public <E> Map<E, T> getListToMap(Function<T, E> key) {
return getListToMap(null, key);
}
@Override
public <E> Map<E, List<T>> getListToMaps(Wrapper<T> queryWrapper, Function<T, E> key) {
return FunctionUtils.group(list(queryWrapper), key);
}
@Override
public <E> Map<E, List<T>> getListToMaps(Function<T, E> key) {
return getListToMaps(null, key);
}
@Override
public T getSingle(LambdaQueryWrapper<T> lambdaQuery) {
if (ValidationUtil.isEmpty(lambdaQuery)) {
return null;
}
lambdaQuery.last("limit 1");
return getOne(lambdaQuery);
}
@Override
public T getSingle(ColumnUtil.SFunction<T, ?> fn, Object value) {
String field = ColumnUtil.getFieldName(fn, "_", 2);
if (ValidationUtil.isEmpty(field) || ValidationUtil.isEmpty(value)) {
return null;
}
QueryWrapper<T> wrapper = new QueryWrapper<>();
wrapper.eq(field, value);
wrapper.last("limit 1");
return getOne(wrapper);
}
@Override
public List<T> getByEntity(Serializable source) {
QueryWrapper<T> queryWrapper = new QueryWrapper<>((T) source);
return baseMapper.selectList(queryWrapper);
}
@Override
public List<T> getList(ColumnUtil.SFunction<T, ?> fn, List values) {
String field = ColumnUtil.getFieldName(fn, "_", 2);
if (ValidationUtil.isEmpty(field) || ValidationUtil.isEmpty(values)) {
return null;
}
QueryWrapper<T> wrapper = new QueryWrapper<>();
wrapper.in(field, values);
return list(wrapper);
}
@Override
public List<T> getList(ColumnUtil.SFunction<T, ?> fn, Object value) {
String field = ColumnUtil.getFieldName(fn, "_", 2);
if (ValidationUtil.isEmpty(field) || ValidationUtil.isEmpty(value)) {
return null;
}
QueryWrapper<T> wrapper = new QueryWrapper<>();
wrapper.eq(field, value);
return list(wrapper);
}
@Override
public int physicalDeleteById(Serializable id) {
if (ValidationUtil.isEmpty(id)) {
return 0;
}
TableInfo tableInfo = TableInfoHelper.getTableInfo(this.entityClass);
return baseMapper.physicalDeleteById(tableInfo.getTableName(), tableInfo.getKeyProperty(), id);
}
@Override
public int physicalDeleteByIds(Collection<? extends Serializable> ids) {
if (ValidationUtil.isEmpty(ids)) {
return 0;
}
TableInfo tableInfo = TableInfoHelper.getTableInfo(this.entityClass);
return baseMapper.physicalDeleteByIds(tableInfo.getTableName(), tableInfo.getKeyProperty(), ids);
}
@Override
public boolean updateWithNullFields(T entity) {
if (entity == null) {
return false;
}
// 使用 UpdateWrapper 指定更新条件,假设有一个叫做 "id" 的主键字段
UpdateWrapper<T> updateWrapper = new UpdateWrapper<>();
TableInfo tableInfo = TableInfoHelper.getTableInfo(entity.getClass());
String keyProperty = tableInfo.getKeyProperty();
Field idField = null;
try {
idField = entity.getClass().getDeclaredField(keyProperty);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
Object idValue = null;
idField.setAccessible(true);
TableId tableIdAnnotation = idField.getAnnotation(TableId.class);
if (tableIdAnnotation != null) {
try {
idValue = idField.get(entity);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
updateWrapper.eq(keyProperty, idValue);
// 遍历实体类的所有字段
for (Field field : entity.getClass().getDeclaredFields()) {
field.setAccessible(true);
if (field.isAnnotationPresent(TableField.class) && field.getAnnotation(TableField.class).exist() == false) {
continue;
}
if (field.getName().equals("updateBy") || field.getName().equals("updateTime")) {
continue;
}
try {
// 获取字段值
Object value = field.get(entity);
// 判断字段值是否为 null
if (value == null) {
// 如果字段值为 null,则将数据库中对应字段置空
String columnName = camelToUnderline(field.getName());
updateWrapper.set(columnName, null);
} else {
// 否则,将字段更新到数据库
String columnName = camelToUnderline(field.getName());
updateWrapper.set(columnName, value);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
// 调用 update 方法执行更新操作
return update(entity,updateWrapper);
}
public static String camelToUnderline(String camelCase) {
StringBuilder result = new StringBuilder();
for (int i = 0; i < camelCase.length(); i++) {
char c = camelCase.charAt(i);
if (Character.isUpperCase(c)) {
result.append("_").append(Character.toLowerCase(c));
} else {
result.append(c);
}
}
return result.toString();
}
}
3、BaseMapper
更改为
BasePlusMapper
java
package com.xx.mybatis;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Param;
import java.io.Serializable;
import java.util.Collection;
/**
* @Author: xueqimiao
* @Date: 2023/3/31 11:15
*/
public interface BasePlusMapper<T> extends BaseMapper<T> {
/**
* 物理删除方法
*/
@Delete("DELETE FROM ${tableName} WHERE ${tableId} = #{id}")
int physicalDeleteById(@Param("tableName") String tableName, @Param("tableId") String tableId, @Param("id") Serializable id);
@Delete("<script>" +
"DELETE FROM ${tableName} WHERE ${tableId} IN " +
"<foreach item='item' index='index' collection='ids' open='(' separator=',' close=')'>" +
"#{item}" +
"</foreach>" +
"</script>")
int physicalDeleteByIds(@Param("tableName") String tableName, @Param("tableId") String keyProperty, @Param("ids") Collection<? extends Serializable> ids);
}
5、自动注入扩展字段
插入数据时自动填充:
create_time
更新数据时自动填充:
update_time
1、注解填充字段
@TableField(fill = FieldFill.INSERT)
DEFAULT
: 默认不处理
INSERT
:插入时填充字段
UPDATE
:更新时填充字段,当设置为这个的时候,即使在MetaObjectHandler.insertFil()的方法中执行了strictInsertFill()操作,执行SQL的时候,也是不会进行操作的。
INSERT_UPDATE
:插入和更新时填充字段
我们到字段上加入了这个注解,可以观察到发的SQL语句,就加上了相应的字段,只是值是null
,所以我们需要写拦截器赋值。
2、自定义实现类 MyMetaObjectHandler
java
package com.xx.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* @Author: xueqimiao
* @Date: 2023/11/8 14:08
*/
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
// 设置方式一
// metaObject.setValue("createTime",new Date());
// metaObject.setValue("updateTime",new Date());
//设置方式二
// 起始版本 3.3.3(推荐)
this.strictInsertFill(metaObject, "createTime", Date.class,new Date());
this.strictInsertFill(metaObject, "updateTime", Date.class,new Date());
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", Date.class,new Date());
}
}
java
package com.xx.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableLogic;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* @Author: xueqimiao
* @Date: 2023/11/1 13:38
*/
@Data
public class BaseModel implements Serializable {
@Schema(description = "创建人")
private String createBy;
@Schema(description = "创建时间")
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@Schema(description = "更新人")
private String updateBy;
@Schema(description = "更新时间")
@TableField(fill = FieldFill.UPDATE)
private Date updateTime;
@Schema(description = "0 正常,1已删除")
@TableLogic
private Integer delFlag;
}
java
@PostMapping("/saveGrade")
@Operation(summary = "添加班级")
public ResultData saveGrade(@RequestBody GradeDTO grade){
if(ValidationUtil.isEmpty(grade)){
return new ResultData(ResultCodeEnum.DATA_EMPTY_FAIL);
}
gradeService.save(ValueUtil.copyFieldValue(grade,GradeModel.class));
return new ResultData();
}
@PostMapping("/updateGrade")
@Operation(summary = "修改班级")
public ResultData updateGrade(@RequestBody GradeDTO grade){
if(ValidationUtil.isEmpty(grade)){
return new ResultData(ResultCodeEnum.DATA_EMPTY_FAIL);
}
gradeService.updateById(ValueUtil.copyFieldValue(grade,GradeModel.class));
return new ResultData();
}
3、注意事项
填充原理是直接给entity的属性设置值:也就是说底层会调用实体类如user的实例的setCreateTime()方法为字段createTime进行赋值。
注解则是指定该属性在对应情况下必有值,如果无值则入库会是null。
MetaObjectHandler提供的默认方法的策略均为:如果属性有值则不覆盖,如果填充值为null则不填充。
字段必须声明TableField注解,属性fill选择对应策略,该声明告知Mybatis-Plus需要预留注入SQL字段
填充处理器MyMetaObjectHandler在 Spring Boot 中需要声明@Component或@Bean注入
要想根据注解FieldFill.xxx和字段名以及字段类型来区分必须使用父类的strictInsertFill或者strictUpdateFill方法。
update(T t,Wrapper updateWrapper)时
t不能为空
,否则自动填充失效。java@PostMapping("/updateGrade2") @Operation(summary = "修改班级2") public ResultData updateGrade2(@RequestBody GradeDTO grade) { if (ValidationUtil.isEmpty(grade)) { return new ResultData(ResultCodeEnum.DATA_EMPTY_FAIL); } LambdaUpdateWrapper<GradeModel> lambdaUpdateWrapper = Wrappers.lambdaUpdate(); lambdaUpdateWrapper.eq(GradeModel::getId, grade.getId()); lambdaUpdateWrapper.set(GradeModel::getGradeName, grade.getGradeName()); gradeService.update(lambdaUpdateWrapper); // gradeService.update(new GradeModel(), lambdaUpdateWrapper); return new ResultData(); }
当自定义mapper方法需要走填充时,建议按下列注解方式添加参数注解(如果使用编译参数保留的情况下,变量名字与注解名字保持一致也行):
数据类型 | 注解 | 示例 |
---|---|---|
Collection | @Param("collection") 或 @Param("coll") | saveXxx(@Param("collection") Collection h2Users) |
List | @Param("list") | saveXxx(@Param("list") List h2Users) |
Array | @Param("array") | saveXxx(@Param("array") H2User[] h2Users) |
实体 | @Param("et") | saveXxx(@Param("et") H2User h2Users) |
6、分页插件
java
@GetMapping("/listPage")
@Operation(summary = "分页查询")
@Parameters({
@Parameter(name = "pageNo", description = "页数", required = true, in = ParameterIn.QUERY),
@Parameter(name = "pageSize", description = "每页条数", required = true, in = ParameterIn.QUERY),
})
public ResultData<Page<GradeModel>> listPage(@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
Page<GradeModel> page = new Page<>(pageNo, pageSize);
Page<GradeModel> gradeModelPage = gradeService.page(page);
return new ResultData(gradeModelPage);
}
发现没效果
java
package com.xx.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Author: xueqimiao
* @Date: 2023/11/8 14:51
*/
@Configuration
public class MybatisPlusConfig {
/**
* 添加分页插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
interceptor.addInnerInterceptor(paginationInnerInterceptor);//如果配置多个插件,切记分页最后添加
//interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); 如果有多数据源可以不配具体类型 否则都建议配上具体的DbType
return interceptor;
}
}
属性介绍
属性名 | 类型 | 默认值 | 描述 |
---|---|---|---|
overflow | boolean | false | 溢出总页数后是否进行处理(默认不处理,参见 插件#continuePage 方法) |
maxLimit | Long | 单页分页条数限制(默认无限制,参见 插件#handlerLimit 方法) | |
dbType | DbType | 数据库类型(根据类型获取应使用的分页方言,参见 插件#findIDialect 方法) | |
dialect | IDialect | 方言实现类(参见 插件#findIDialect 方法) |
7、pagehelper
PageHelper是一个用于MyBatis的开源分页插件,它提供了非常方便的分页功能,使你能够轻松地在MyBatis查询中进行分页操作。PageHelper可以帮助你处理分页查询时的繁琐工作,包括计算总记录数、生成分页SQL语句等。以下是PageHelper的一些主要特点和用法:
- 简单的配置:PageHelper提供了一个简单的配置方式,你只需要在MyBatis的XML配置文件或者Java代码中配置分页参数,即可启用分页功能。
- 自动计算总记录数:PageHelper会自动在查询前执行一条
COUNT
查询,以便计算总记录数,这样你无需手动编写额外的SQL语句来获取总记录数。 - 支持多种数据库:PageHelper支持多种数据库,包括MySQL、Oracle、SQL Server等主流数据库,因此你可以在不同数据库上使用相同的分页插件。
- 多种分页方式:PageHelper支持多种分页方式,包括传统的物理分页和逻辑分页,你可以根据需要选择适合你的分页方式。
- 分页参数灵活配置:你可以通过PageHelper配置不同的分页参数,例如页码、每页显示的记录数等,以满足不同的分页需求。
- 多种排序方式:PageHelper支持多种排序方式,包括升序、降序等,可以在查询时指定排序字段。
java
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
java
@GetMapping("/listPage2")
@Operation(summary = "分页查询2")
@Parameters({
@Parameter(name = "pageNo", description = "页数", required = true, in = ParameterIn.QUERY),
@Parameter(name = "pageSize", description = "每页条数", required = true, in = ParameterIn.QUERY),
})
public ResultData listPage2(@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
Page startPage = PageHelper.startPage(pageNo, pageSize);
gradeService.list();
PageInfo pageInfo = new PageInfo<>(startPage);
return new ResultData(pageInfo);
}
8、项目配置
1、pom.xml
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>
<groupId>org.example</groupId>
<artifactId>dtx_test</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.0</version>
<relativePath/>
</parent>
<properties>
<!-- 依赖版本号 -->
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 组件版本号 -->
<hutool.version>5.8.16</hutool.version>
<druid.version>1.1.18</druid.version>
<persistence-api.version>1.0.2</persistence-api.version>
<mysql.version>8.0.27</mysql.version>
<swagger3.version>3.0.0</swagger3.version>
<mybatis-plus.spring.version>3.5.3</mybatis-plus.spring.version>
<lombok.version>1.18.26</lombok.version>
<boottest.version>3.0.5</boottest.version>
</properties>
<dependencies>
<!--web + actuator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--swagger3-->
<!--<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.1.0</version>
</dependency>-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.spring.version}</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!--persistence-->
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>${persistence-api.version}</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--boot-test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--hutool-all-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>com.xx</groupId>
<artifactId>xx-common-core</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2、application.yml
properties
server:
port: 7777
servlet:
context-path: /dtx-test
spring:
application:
name: dtx_test
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/xx_db2023?connectTimeout=6000&socketTimeout=6000&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username: root
password: mac_root
# main:
# allow-circular-references: true
# mybatis-plus相关配置
mybatis-plus:
# global-config:
# db-config:
# id-type: auto
# xml扫描,多个目录用逗号或者分号分隔(告诉 Mapper 所对应的 XML 文件位置)
mapper-locations: classpath:mapper/*Mapper.xml
# type-enums-package: com.xx.enums
configuration:
# 是否开启自动驼峰命名规则映射:从数据库列名到Java属性驼峰命名的类似映射
map-underscore-to-camel-case: true
# 如果查询结果中包含空值的列,则 MyBatis 在映射的时候,不会映射这个字段
call-setters-on-nulls: true
# 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
cache-enabled: true
db-config:
logic-delete-field: del_flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
#Mybatis输出sql日志
logging:
level:
com.xx.mapper: info
#xxl-job配置
xxl:
job:
admin:
addresses: http://127.0.0.1:8080/xxl-job-admin
accessToken: default_token
executor:
appname: xxl-job-executor-dtx
address:
ip:
port: 9988
logpath: ./xxl-job/jobhandler
logretentiondays: 30