当前位置: 首页 > news >正文

基于Mybatis-Plus扩展批量插入或更新InsertOrUpdateBath

前言: mybatis-plus 是一款很好用的crud基础框架,但是我在api中没有找到插入或者更新,那么我想着基于mybatis-plus 自定义一个方法出来用,因为插入或者更新在字段数量多的时候写xml是非常麻烦的事情。

传统写法:

INSERT INTO test(`id`,`name`,`address`)
VALUES('4','修改10','北京'),
    ( '1', '张三' ,1) 
ON DUPLICATE KEY UPDATE
name=VALUES(name),address=VALUES(address);

 

基于MP的写法,是需要用到MP里面的SQL注入器

MP SQL 注入器文档地址: SQL注入器 | MyBatis-Plus

 1. 新建自定义注解 DuplicateSql

说明:自定义注解是为了插入更新基于那些字段(自定更新字段使用)

/**
 * @author yueF_L
 * @version 1.0
 * @date 2022-08-30 10:21
 * 自定义注解DuplicateSql
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DuplicateSql {
    /**
     * 列名称,对应数据库字段
     *
     * @return
     */
    String columnName() default "";
}

2. 自定义批量插入更新方法实现

/**
 * 批量插入更新方法实现
 */
public class MysqlInsertOrUpdateBath extends AbstractMethod {

    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        final String sql = "<script>insert into %s %s values %s ON DUPLICATE KEY UPDATE %s</script>";
        final String tableName = tableInfo.getTableName();
        final String filedSql = prepareFieldSql(tableInfo);
        final String modelValuesSql = prepareModelValuesSql(tableInfo);
        final String modelKeySql = prepareDuplicateKeySql(modelClass);
        final String duplicateKeySql = prepareDuplicateKeySql(tableInfo);
        final String sqlResult = String.format(sql, tableName, filedSql, modelValuesSql, StringUtils.isBlank(modelKeySql) ? duplicateKeySql : modelKeySql);
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass);
        return this.addInsertMappedStatement(mapperClass, modelClass, "mysqlInsertOrUpdateBath", sqlSource, new NoKeyGenerator(), null, null);
    }

    /**
     * 根据自定义字段更新
     *
     * @param modelClass
     * @return
     */
    private String prepareDuplicateKeySql(Class<?> modelClass) {
        final StringBuilder duplicateKeySql = new StringBuilder();
        // 获取所有的字段信息
        Field[] declaredFields = modelClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            DuplicateSql duplicateSql = declaredField.getAnnotation(DuplicateSql.class);

            if (ObjectUtil.isNotEmpty(duplicateSql)) {
                String columnName = duplicateSql.columnName();
                duplicateKeySql.append(columnName).append("=values(").append(columnName).append("),");
            }
        }
        if (StringUtils.isNotBlank(duplicateKeySql)) {
            duplicateKeySql.delete(duplicateKeySql.length() - 1, duplicateKeySql.length());
        }
        return duplicateKeySql.toString();
    }

    /**
     * 准备ON DUPLICATE KEY UPDATE sql
     * 根据全字段更新(不推荐使用)
     *
     * @param tableInfo
     * @return
     */
    private String prepareDuplicateKeySql(TableInfo tableInfo) {
        final StringBuilder duplicateKeySql = new StringBuilder();
        if (!StringUtils.isEmpty(tableInfo.getKeyColumn())) {
            duplicateKeySql.append(tableInfo.getKeyColumn()).append("=values(").append(tableInfo.getKeyColumn()).append("),");
        }

        tableInfo.getFieldList().forEach(x -> {
            duplicateKeySql.append(x.getColumn())
                .append("=values(")
                .append(x.getColumn())
                .append("),");
        });
        duplicateKeySql.delete(duplicateKeySql.length() - 1, duplicateKeySql.length());
        return duplicateKeySql.toString();
    }

    /**
     * 准备属性名
     *
     * @param tableInfo
     * @return
     */
    private String prepareFieldSql(TableInfo tableInfo) {
        StringBuilder fieldSql = new StringBuilder();
        fieldSql.append(tableInfo.getKeyColumn()).append(",");
        tableInfo.getFieldList().forEach(x -> {
            fieldSql.append(x.getColumn()).append(",");
        });
        fieldSql.delete(fieldSql.length() - 1, fieldSql.length());
        fieldSql.insert(0, "(");
        fieldSql.append(")");
        return fieldSql.toString();
    }

    private String prepareModelValuesSql(TableInfo tableInfo) {
        final StringBuilder valueSql = new StringBuilder();
        valueSql.append("<foreach collection=\"list\" item=\"item\" index=\"index\" open=\"(\" separator=\"),(\" close=\")\">");
        if (!StringUtils.isEmpty(tableInfo.getKeyProperty())) {
            valueSql.append("#{item.").append(tableInfo.getKeyProperty()).append("},");
        }
        tableInfo.getFieldList().forEach(x -> valueSql.append("#{item.").append(x.getProperty()).append("},"));
        valueSql.delete(valueSql.length() - 1, valueSql.length());
        valueSql.append("</foreach>");
        return valueSql.toString();
    }
}

3. 注册自定义方法SQL注入器

/**
 * @author yueF_L
 * @version 1.0
 * @date 2022-08-29 17:18
 * 注册自定义方法SQL注入器
 */
@Component
public class CustomizedSqlInjector extends DefaultSqlInjector {
    /**
     * 如果只需增加方法,保留mybatis plus自带方法,
     * 可以先获取super.getMethodList(),再添加add
     */
    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
        List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);
        methodList.add(new MysqlInsertOrUpdateBath());
        return methodList;
    }
}

4. 自定义Mapper RootMapper

/**
 * @author yueF_L
 * @version 1.0
 * @date 2022-08-29 17:11
 * 根Mapper,给表Mapper继承用的,可以自定义通用方法
 */
public interface RootMapper<T> extends BaseMapper<T> {


    /**
     * 自定义批量新增或更新
     * 需要配合自定注解 DuplicateSql 使用
     *
     * @param list
     * @return
     */
    Boolean mysqlInsertOrUpdateBath(@Param("list") List<T> list);

}

相关文章:

  • LeetCode·701.二叉搜索树中的插入操作·递归
  • 数据结构试题(一)
  • DevSecOps 安全即代码基础指南
  • js字符串对比之localeCompare()方法-对字符串进行排序——大于0-升序、小于0-降序 对el-table的列进行排序sort-change
  • Vue开发环境安装
  • springboot小型命题系统毕业设计源码011508
  • 61-70==c++知识点
  • 一文快速上手 Nacos 注册中心+配置中心!
  • 云扩RPA携手中联教育引领财务机器人教学创新
  • 入阿里P6?最少啃完这本阿里最新Java多线程编程手册,建议收藏
  • 【毕业设计】深度学习人脸表情识别系统 - python OpenCV
  • 基于HTML仿华为手机网站电商项目的设计与实现
  • 【Java基础】方法重写、修饰符、权限修饰符及final、static关键字
  • 【C++】之const
  • Stream 的使用,我觉得使用它是非常方便的
  • [deviceone开发]-do_Webview的基本示例
  • [分享]iOS开发 - 实现UITableView Plain SectionView和table不停留一起滑动
  • 【node学习】协程
  • 【跃迁之路】【444天】程序员高效学习方法论探索系列(实验阶段201-2018.04.25)...
  • C++类中的特殊成员函数
  • dva中组件的懒加载
  • FastReport在线报表设计器工作原理
  • js数组之filter
  • Less 日常用法
  • Sass 快速入门教程
  • spring boot下thymeleaf全局静态变量配置
  • ⭐ Unity 开发bug —— 打包后shader失效或者bug (我这里用Shader做两张图片的合并发现了问题)
  • 机器学习中为什么要做归一化normalization
  • 前端面试题总结
  • 中文输入法与React文本输入框的问题与解决方案
  • #Linux(Source Insight安装及工程建立)
  • #stm32整理(一)flash读写
  • $ git push -u origin master 推送到远程库出错
  • (4)Elastix图像配准:3D图像
  • (C语言)二分查找 超详细
  • (ibm)Java 语言的 XPath API
  • (Matalb回归预测)PSO-BP粒子群算法优化BP神经网络的多维回归预测
  • (windows2012共享文件夹和防火墙设置
  • (笔试题)合法字符串
  • (超简单)构建高可用网络应用:使用Nginx进行负载均衡与健康检查
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • (企业 / 公司项目)前端使用pingyin-pro将汉字转成拼音
  • (十八)devops持续集成开发——使用docker安装部署jenkins流水线服务
  • (一) springboot详细介绍
  • (转) 深度模型优化性能 调参
  • (转)Linq学习笔记
  • (转)创业的注意事项
  • (转)从零实现3D图像引擎:(8)参数化直线与3D平面函数库
  • (转载)虚幻引擎3--【UnrealScript教程】章节一:20.location和rotation
  • .bat批处理(三):变量声明、设置、拼接、截取
  • .NET Core日志内容详解,详解不同日志级别的区别和有关日志记录的实用工具和第三方库详解与示例
  • .NET/C# 避免调试器不小心提前计算本应延迟计算的值
  • .NET/C# 反射的的性能数据,以及高性能开发建议(反射获取 Attribute 和反射调用方法)
  • .NET设计模式(8):适配器模式(Adapter Pattern)
  • .NET微信公众号开发-2.0创建自定义菜单