整合mybatis-plus,并记录各种用法

整合mybatis-plus

基本使用

导入依赖

            <!--mybatisPlus依赖-->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.5.3</version>
            </dependency>

配置

mybatis-plus:
  configuration:
    #日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    #开启驼峰命名
    map-underscore-to-camel-case: true
  global-config:
    db-config:
      #逻辑删除字段
      logic-delete-field: delFlag
      #代表已经删除得值
      logic-delete-value: 1
      #代表未删除得值
      logic-not-delete-value: 0
      #主键自增
      id-type: auto

配置类

package com.jjking.handler.mybatisplus;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.jjking.utils.SecurityUtils;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * @author 35238
 * @date 2023/7/26 0026 20:52
 */
@Component
//这个类是用来配置mybatis的字段自动填充。用于'发送评论'功能,由于我们在评论表无法对下面这四个字段进行插入数据(原因是前端在发送评论时,没有在
//请求体提供下面四个参数,所以后端在往数据库插入数据时,下面四个字段是空值),所有就需要这个类来帮助我们往下面这四个字段自动的插入值,
//只要我们更新了评论表的字段,那么无法插入值的字段就自动有值了
public class MyMetaObjectHandler implements MetaObjectHandler {

    @Override
    //只要对数据库执行了插入语句,那么就会执行到这个方法
    public void insertFill(MetaObject metaObject) {
        Long userId = null;
        try {
            //获取用户id
            userId = SecurityUtils.getUserId();
        } catch (Exception e) {
            e.printStackTrace();
            userId = -1L;//如果异常了,就说明该用户还没注册,我们就把该用户的userid字段赋值d为-1
        }
        //自动把下面四个字段新增了值。
        this.setFieldValByName("createTime", new Date(), metaObject);
        this.setFieldValByName("createBy",userId , metaObject);
        this.setFieldValByName("updateTime", new Date(), metaObject);
        this.setFieldValByName("updateBy", userId, metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("updateTime", new Date(), metaObject);
        this.setFieldValByName(" ", SecurityUtils.getUserId(), metaObject);
    }
}

主启动类要加上mapperscan

/**
 * 前台启动类
 * @author jjking
 * @date 2023-12-01 22:41
 */
@SpringBootApplication
@MapperScan(basePackages = "com.sky.mapper")
@EnableScheduling
@EnableSwagger2
public class BlogApplication {
    public static void main(String[] args) {
        SpringApplication.run(BlogApplication.class,args);
    }
}

这里的mapperscan我换成
**@MapperScan(basePackages = “com.sky.mapper”)**才能起作用,不然还是没作用

各类上的注解

mapper

/**
 * @author jjking
 * @date 2023-12-02 12:01
 */
@Mapper
public interface ArticleMapper extends BaseMapper<Article> {
}

service

/**
 * 文章表(Article)表服务接口
 *
 * @author makejava
 * @since 2023-12-02 11:59:42
 */
public interface ArticleService extends IService<Article> {}

service实现类

@Service("articleService")
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements ArticleService {
}

自动填充功能设置

一共两步

填写注解

public class User {

    // 注意!这里需要标记为填充字段
    @TableField(fill = FieldFill.INSERT)
    private String fillField;

    ....
}

这里的fill有四个注解,看字面意思也很好理解

/**
 * 默认不处理
 */
DEFAULT,
/**
 * 插入填充字段
 */
INSERT,
/**
 * 更新填充字段
 */
UPDATE,
/**
 * 插入和更新填充字段
 */
INSERT_UPDATE
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert fill ....");
        this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用)
        // 或者
        this.strictInsertFill(metaObject, "createTime", () -> LocalDateTime.now(), LocalDateTime.class); // 起始版本 3.3.3(推荐)
        // 或者
        this.fillStrategy(metaObject, "createTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug)
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start update fill ....");
        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐)
        // 或者
        this.strictUpdateFill(metaObject, "updateTime", () -> LocalDateTime.now(), LocalDateTime.class); // 起始版本 3.3.3(推荐)
        // 或者
        this.fillStrategy(metaObject, "updateTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug)
    }
}

最后写一个这样的配置类就行,一个是写插入的逻辑,一个是写新增的逻辑

改进自动填充功能

每次我们都要在字段上写注解,这是一件很费时间的事,我在
https://blog.csdn.net/weixin_44684272/article/details/126067864
他写的全局设置注解,中看到的操作,这就是我想要的操作
特此记录一下

写配置类
先在mybaitisconfig里边写一个配置

@Configuration
public class MybatisPlusConfig {

    /**
     * 添加更新拦截器,加入全局插入字段
     * @return
     */
    @Bean
    public UpdateInterceptor updateInterceptor() {
        return new UpdateInterceptor();
    }
}

然后创建一个拦截器,似乎是mybatisplus的sql注入器,什么的,我的理解像是拦截器,在我们写sql的时候,他拦截一下,我门就能在要执行sql的时候,写入我们自己的逻辑

package com.sky.handler.mybatis;

import com.baomidou.mybatisplus.extension.handlers.AbstractSqlParserHandler;
import com.sky.context.BaseContext;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;

import java.lang.reflect.Field;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;

/**
 * @author : [LiuYanQiang]
 * @version : [v1.0]
 * @className : UpdateInterceptor
 * @description : [自动给创建时间个更新时间加值]
 * @createTime : [2022/1/12 9:09]
 * @updateUser : [LiuYanQiang]
 * @updateTime : [2022/1/12 9:09]
 * @updateRemark : [描述说明本次修改内容]
 */
@Intercepts(value = {@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
public class UpdateInterceptor extends AbstractSqlParserHandler implements Interceptor {

    /**
     * 创建时间
     */
    private static final String CREATE_TIME = "createTime";
    /**
     * 更新时间
     */
    private static final String UPDATE_TIME = "updateTime";
    /**
     * 创建时间
     */
    private static final String CREATE_USER = "createUser";
    /**
     * 更新user
     */
    private static final String UPDATE_USER = "updateUser";

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        // SQL操作命令,是什么操作,update或者insert
        SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
        // 获取新增或修改的对象参数
        Object parameter = invocation.getArgs()[1];
        // 获取对象中所有的私有成员变量(对应表字段)
        Field[] declaredFields = parameter.getClass().getDeclaredFields();
        if (parameter.getClass().getSuperclass() != null) {
            Field[] superField = parameter.getClass().getSuperclass().getDeclaredFields();
            declaredFields = ArrayUtils.addAll(declaredFields, superField);
        }
        // mybatis plus判断
        boolean plus= parameter.getClass().getDeclaredFields().length == 1 && parameter.getClass().getDeclaredFields()[0].getName().equals("serialVersionUID");

        //兼容mybatis plus
        if (plus) {
            Map<String, Object> updateParam = (Map<String, Object>) parameter;
            Class<?> updateParamType = updateParam.get(updateParam.keySet().iterator().next()).getClass();
            declaredFields = updateParamType.getDeclaredFields();
            if (updateParamType.getSuperclass() != null) {
                Field[] superField = updateParamType.getSuperclass().getDeclaredFields();
                declaredFields = ArrayUtils.addAll(declaredFields, superField);
            }
        }

        //获得所有列
        String fieldName = null;
        for (Field field : declaredFields) {
            //获得字段名
            fieldName = field.getName();
            //设置创建时间字段
            if (Objects.equals(CREATE_TIME, fieldName)) {
                if (SqlCommandType.INSERT.equals(sqlCommandType)) {
                    field.setAccessible(true);
                    field.set(parameter, LocalDateTime.now());
                }
            }
            //设置创建user
            if (Objects.equals(CREATE_USER, fieldName)) {
                if (SqlCommandType.INSERT.equals(sqlCommandType)) {
                    field.setAccessible(true);
                    field.set(parameter, BaseContext.getCurrentId());
                }
            }
            //设置更新的时间
            if (Objects.equals(UPDATE_TIME, fieldName)) {
                if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
                    field.setAccessible(true);
                    //兼容mybatis plus的update
                    if (plus) {
                        Map<String, Object> updateParam = (Map<String, Object>) parameter;
                        field.set(updateParam.get(updateParam.keySet().iterator().next()), LocalDateTime.now());
                    } else {
                        field.set(parameter, LocalDateTime.now());
                    }
                }
            }
            //设置更新人
            if (Objects.equals(UPDATE_USER, fieldName)) {
                if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
                    field.setAccessible(true);
                    //兼容mybatis plus的update
                    if (plus) {
                        Map<String, Object> updateParam = (Map<String, Object>) parameter;
                        field.set(updateParam.get(updateParam.keySet().iterator().next()), BaseContext.getCurrentId());
                    } else {
                        field.set(parameter, BaseContext.getCurrentId());
                    }
                }
            }
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof Executor) {
            return Plugin.wrap(target, this);
        }
        return target;
    }

    @Override
    public void setProperties(Properties properties) {
    }
}

我在他的基础之上,再加上了updateUser 和 createUser,其实这个代码看起来很难,但其实不是特别难理解,虽然我也不是很懂他这些事从哪里知道的,我估计要去深入mybatis-plus的源码才能搞懂,我这里特别记录一下我自己的理解

首先我们的目标有两个,一个是我们要知道,此时的sql是insert还是update,还是其他的什么的,也就是我们要知道此时是增删改查的哪一种,也就是我们这里的SqlCommandType

第二个,就是我们要得到操作的到底是哪些字段,得到字段值,我们才可以去匹配是不是我们要写公共字段,也就是这里的Field[] declaredFields

然后得到这两样之后,就先遍历表的字段,然后判断此时是什么操作,如果是update或者insert,那么就要判断是不是我们要的字段
这里的逻辑还是很简单的,但是其中用的类,得等我有时间去挖掘mybatis,我再来这里更新一下

分页功能设置

写配置

/**
 * @author jjking
 * @date 2024-01-22 22:39
 */
@Configuration
public class MybatisPlusConfig {

    /**
     * 添加分页插件
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//如果配置多个插件,切记分页最后添加
        return interceptor;
    }
}

使用示例

    @Override
    public PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO) {
        LambdaQueryWrapper<Employee> employeeWrapper = new LambdaQueryWrapper<>();
        employeeWrapper.like(Objects.nonNull(employeePageQueryDTO.getName()), Employee::getName, employeePageQueryDTO.getName());

        //分页
        Page<Employee> page = new Page<>(employeePageQueryDTO.getPage(), employeePageQueryDTO.getPageSize());
        page(page, employeeWrapper);

        PageResult pageResult = new PageResult(page.getTotal(), page.getRecords());
        return pageResult;
    }

这里使用LambdaQueryWrapper,可以简写一些代码
这里的pageResult可以自定义,一个自定义类,因为前端页面需要count,也就是总共的记录数
还有就是records就是实际的数据

/**
 * 封装分页查询结果
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PageResult implements Serializable {

    private long total; //总记录数

    private List records; //当前页数据集合

}

逻辑删除设置

如下实现逻辑删除
首先我们得再数据库中的表加上逻辑删除字段
在这里插入图片描述
特别要注意这里的问题,如果我们在配置中写了驼峰命名的规则的化,那么我们在前端的字段应该是delFlag
在实体类Category中

    //删除标志(0代表未删除,1代表已删除)
    private Integer delFlag;

然后我们在业务中进行删除
我测试过了,无论是Service的removeByid,还是mapper的deleteByid都能触发逻辑删除字段

    @ApiOperation("删除分类")
    @DeleteMapping
    public Result delete(Long id) {
        log.info("删除分类 参数为{}",id);
        categoryService.removeById(id);
        return Result.success();
    }

我们实现了逻辑删除的功能,我们就得想.那要如何实现真正的删除呢,我们总要有这个功能把,不能真的不给删除吧,所以,再次记录真正删除的方法

这里mybtisplus并没有实现这个功能,我看了很久,没有实现的,那么就只能物理删除,自己写mapper,写一个sql,虽然这也不难,但是也蛮麻烦的,还是希望官方能实现这个功能,懒得写sql

我们得现在mapper中写一个方法

@Mapper
public interface CategoryMapper extends BaseMapper<Category> {

    /**
     * 实现物理删除
     * @param id
     * @return
     */
    int delete(@Param("id") Long id);
}

在生成的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.sky.mapper.CategoryMapper">


    <delete id="delete">
        delete from category where id = #{id}
    </delete>
</mapper>

直接删除
这样就能实现真正的物理删除了

小问题

插入一条数据后,需要得到id

有的时候,我们有这样的需求,我门调用了save之后,需要得到save之后的id,我就去找怎么得到这个id,后来发现,mybatis-plus 已经设置好了,就在原来的实体类上,已经设置好了

也就是save(实体类),会在这个实体类上如果有id的化,会设置好的

我估计底层是设置主键的值,应该不是按照名字来回显这个值的,这样才合理一点

  • 10
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

憨憨小江

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值