Mybatis-Plus利用Sql注入器批量插入更新

Mybatis-Plus是在Mybatis持久层框架上封装的一层非常好用的工具,最近因为想要在Mapper里加入自己自定义的通用方法,所以用到了Mybatis-Plus的Sql注入器。Sql注入器的作用是可以实现自定义的sql脚本并注入到MappedStatement里,从而达到动态拼装sql并生成Mapper接口的目的。这种方式与自己写一个通用Mapper的不同在于,Mybatis-Plus提供的AbstractMethod方法类,实现的接口里可以获取到表信息,我们可以利用它们做批量插入和批量更新的sql拼装。同时,Mybatis-Plus也提供了自带的一些AbstractMethod实现类。下面我以批量更新和批量插入两个示例,贴出代码供大家参考。

自定义批量更新方法 UpdateBatchMethod.java。重载injectMappedStatement方法,此方法可以生成拼接批量更新sql的脚本。

import cn.hutool.db.Entity;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;public class UpdateBatchMethod extends AbstractMethod {/**update user setname=(CASEWHEN id=1 THEN '张三'WHEN id=2 THEN '李四'end),age =(CASEWHEN id=1 THEN '2'WHEN id=2 THEN '2'end) where id in (1,2);<script>update user <trim prefix="set" suffixOverrides=","><trim prefix="name =(case" suffix="end),"><foreach collection="list" item="item" ><if test="item.name!=null">when id=#{item.id} then #{item.name}</if></foreach>else name</trim><trim prefix="age =(case" suffix="end),"><foreach collection="list" item="item" ><if test="item.age!=null">when id=#{item.id} then #{item.age}</if></foreach>else age</trim></trim>where id in <foreach collection="list" item="item" separator="," open="(" close=")">#{item.id} </foreach> </script>*/@Overridepublic MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {final String sql = "<script>\n update %s %s \n where id in \n <foreach collection=\"list\" item=\"item\" separator=\",\" open=\"(\" close=\")\">\n #{item.id} </foreach> \n </script>";final String valueSql = prepareValuesSql(tableInfo);final String sqlResult = String.format(sql, tableInfo.getTableName(), valueSql);SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass);return this.addUpdateMappedStatement(mapperClass, modelClass, "updateBatch", sqlSource);}private String prepareValuesSql(TableInfo tableInfo) {final StringBuilder valueSql = new StringBuilder();valueSql.append("<trim prefix=\"set\" suffixOverrides=\",\">\n");tableInfo.getFieldList().forEach(x -> {valueSql.append("<trim prefix=\"").append(x.getColumn()).append(" =(case \" suffix=\"end),\">\n");valueSql.append("<foreach collection=\"list\" item=\"item\" >\n");valueSql.append("when id=#{item.id} then ifnull(#{item.").append(x.getProperty()).append("},").append(x.getColumn()).append(")\n");valueSql.append("</foreach>\n");valueSql.append("else ").append(x.getColumn());valueSql.append("</trim>\n");});valueSql.append("</trim>\n");return valueSql.toString();}
}

自定义Sql注入器 InsertBatchSqlInjector.java。将上面的批量更新方法对象添加到默认sql注入器的方法列表。一同添加的还有mybatis-plus自带的批量新增方法。

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.extension.injector.methods.additional.InsertBatchSomeColumn;import java.util.List;public class InsertBatchSqlInjector extends DefaultSqlInjector {@Overridepublic List<AbstractMethod> getMethodList(Class<?> mapperClass) {List<AbstractMethod> methodList = super.getMethodList(mapperClass);methodList.add(new InsertBatchSomeColumn());methodList.add(new UpdateBatchMethod());return methodList;}
}

注入Sql注入器 MybatisPlusConfig.java。将上面我们自定义的sql注入器注入到Spring容器里。

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class MybatisPlusConfig {@Beanpublic InsertBatchSqlInjector insertBatchSqlInjector() {return new InsertBatchSqlInjector();}
}

自定义BaseMapper。

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;import java.util.List;public interface MyBaseMapper<T> extends BaseMapper<T> {// 批量插入int insertBatchSomeColumn(@Param("list") List<T> batchList);// 批量更新int updateBatch(@Param("list") List<T> list);
}

然后,在业务Mapper对象上,将继承类从BaseMapper改为上面我们创建好的MyBaseMapper

自定义ServiceImpl

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.smcaiot.wcpa.safedata.mapper.MyBaseMapper;
import org.springframework.transaction.annotation.Transactional;import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;public class MyServiceImpl<M extends MyBaseMapper<T>, T> extends ServiceImpl<M, T> {@Overridepublic boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize) {if (CollUtil.isEmpty(entityList)) {return false;}List<T> updates = entityList.stream().filter(x -> !isIdNull(x)).collect(Collectors.toList());List<T> inserts = entityList.stream().filter(x -> isIdNull(x)).collect(Collectors.toList());int count = 0;List<T> tmpList = new ArrayList<>();if (CollUtil.isNotEmpty(inserts)) {for (T insert : inserts) {int i = tmpList.size();if (i >= 1 && i % batchSize == 0) {count += getBaseMapper().insertBatchSomeColumn(tmpList);tmpList.clear();}tmpList.add(insert);}count += getBaseMapper().insertBatchSomeColumn(tmpList);tmpList.clear();}if (CollUtil.isNotEmpty(updates)) {for (T update : updates) {int i = tmpList.size();if (i >= 1 && i % batchSize == 0) {count += getBaseMapper().updateBatch(tmpList);tmpList.clear();}tmpList.add(update);}count += getBaseMapper().updateBatch(tmpList);tmpList.clear();}return count > 0;}private boolean isIdNull(Object obj) {return Objects.isNull(BeanUtil.getProperty(obj, "id"));}
}

将业务Service的继承类改成上面我们生成的MyServiceImpl.java

单元测试配置

import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.smcaiot.wcpa.safedata.app.InsertBatchSqlInjector;
import com.smcaiot.wcpa.safedata.app.MybatisPlusConfig;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;import javax.sql.DataSource;@Configuration
@MapperScan({"com.xxx.mapper"})
@ComponentScan(value = "com.xxx.config", includeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {MybatisPlusConfig.class})
}, useDefaultFilters = false)
public class TestMybatisPlusConfig {@Autowiredprivate InsertBatchSqlInjector insertBatchSqlInjector;@Beanpublic MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean() throws Exception {MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dataSource());sqlSessionFactoryBean.setGlobalConfig(globalConfig());return sqlSessionFactoryBean;}@Beanpublic SqlSession sqlSession() throws Exception {return mybatisSqlSessionFactoryBean().getObject().openSession(true);}@Beanpublic GlobalConfig globalConfig() throws Exception {GlobalConfig globalConfig = new GlobalConfig();globalConfig.setSqlInjector(insertBatchSqlInjector);return globalConfig;}@Beanpublic DataSource dataSource() {HikariConfig config = new HikariConfig();config.setJdbcUrl("jdbc:mysql://dburl:3306/dbname?useSSL=false&characterEncoding=utf-8&serverTimezone=GMT%2B8&allowMultiQueries=true&rewriteBatchedStatements=true");config.setUsername("username");config.setPassword("password");config.setDriverClassName("com.mysql.cj.jdbc.Driver");return new HikariDataSource(config);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/225485.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[css] flex wrap 九宫格布局

<div class"box"><ul class"box-inner"><li>九宫格1</li><li>九宫格2</li><li>九宫格3</li><li>九宫格4</li><li>九宫格5</li><li>九宫格6</li><li>九宫格7&l…

【算法提升—力扣每日一刷】五日总结【12/06--12/10】

文章目录 2023/12/06力扣每日一刷&#xff1a;[206. 反转链表](https://leetcode.cn/problems/reverse-linked-list/) 2023/12/07力扣每日一刷&#xff1a;[203. 移除链表元素](https://leetcode.cn/problems/remove-linked-list-elements/)力扣今日两刷&#xff1a;[19. 删除链…

iOS_给View的部分区域截图 snapshot for view

文章目录 1.将整个view截图返回image&#xff1a;2.截取view的部分区域&#xff0c;返回image&#xff1a;3.旧方法&#xff1a;4.Tips参考&#xff1a; 1.将整个view截图返回image&#xff1a; 这些 api 已被废弃&#xff0c;所以需要判断 iOS 版本 写两套代码&#xff1a; R…

NVMe over Fabrics with SPDK with iRDMA总结 - 1

1.0 Introduction简介 NVM Express* (NVMe*) drives are high-speed, low-latency, solid-state drives (SSDs), that connect over the server Peripheral Component Interconnect Express* (PCIe*) bus. NVM Express* (NVMe*) 硬盘是高速、低延迟的固态硬盘 (SSD),通过服…

轻松制作健身预约小程序

如果你想制作一个健身预约小程序&#xff0c;实现高效预约与健身管理&#xff0c;可以按照以下步骤进行操作。 第一步&#xff1a;注册登录乔拓云平台&#xff0c;进入后台 第二步&#xff1a;点击【轻应用小程序】&#xff0c;进入设计小程序页面。 第三步&#xff1a;在设计小…

Python基础(八、random模块探秘)

大家好&#xff0c;今天我要和你们聊一聊一个非常有趣的Python模块——random。它就像是一个疯狂的抽签者&#xff0c;总是在背后悄悄地为我们制造出各种各样的随机事件。让我们一起来揭开random的神秘面纱&#xff0c;看看它到底能带给我们哪些惊喜&#xff01; 文章目录 1. …

uniGUI学习之Cookie

UniApplication.Cookies.SetCookie( const ACookieName: string, const AValue: string, AExpires: TDateTime 0, ASecure: Boolean False, AHTTPOnly: Boolean False, const APath: string / )

多汗症的预防措施有哪些?

多汗症的预防措施主要包括以下几个方面&#xff0c;通过这些措施&#xff0c;可以有效地减少多汗症的发生&#xff0c;提高生活质量。 一、保持身体清洁 保持身体清洁是预防多汗症的重要措施之一。每天洗澡&#xff0c;特别是在运动或出汗后&#xff0c;及时更换衣物&#xf…

Python-类视图和蓝图

目录 一.类视图 二.蓝图 一.类视图 在Flask中&#xff0c;可通过视图函数展示视图 http://t.csdnimg.cn/r6IFG 也可基于类实现&#xff0c;类视图的好处是支持继承。标准类视图是继承flask.views模块中基类View的子类 from flask.views import View 该子类中必须重写View…

查找一个时间序列中一个具体时刻的所有值Series.at_time()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 查找时间序列中 指定的具体时刻 对应的所有元素 Series.at_time() [太阳]选择题 以下代码的输出结果中正确的是? import pandas as pd i pd.date_range(2023-12-10, periods4, freq12H) r p…

tcp/ip协议2实现的插图,数据结构5 (22 - 章)

(103) 103 二二1 协议控制块 结构 file, socket , rawcb , inpcb , tcpcb 之间的联系 (104) (105)

转载:TableView性能优化

转载&#xff1a;TableView性能优化 原文链接&#xff1a;https://juejin.cn/post/6955731915672387592 tableView性能优化 Cell重用、标识重用 使用 static 修饰重用标识名称能够保证这个标识只会创建一次&#xff0c;提高性能。接着调用dequeueReusableCellWithIdentifie…

超越架构师!消息通知系统优化设计

5 收集联系信息流程 为发送通知&#xff0c;需收集各种信息如移动设备令牌、email、phone和第三方通道信息。 用于存储联系信息的简化的数据库表模式。它是个带有电子邮件、电话、设备令牌和外部通道的单个NoSQL DynamoDB表。Contacts table schema&#xff1a; device_tokens…

LeetCode(63)旋转链表【链表】【中等】

目录 1.题目2.答案3.提交结果截图 链接&#xff1a; 旋转链表 1.题目 给你一个链表的头节点 head &#xff0c;旋转链表&#xff0c;将链表每个节点向右移动 k 个位置。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], k 2 输出&#xff1a;[4,5,1,2,3]示例 2&…

C语言--求数组的最大值和最小值【两种方法】

&#x1f357;方法一&#xff1a;用for循环遍历数组&#xff0c;找出最大值与最小值 &#x1f357;方法二&#xff1a;用qsort排序&#xff0c;让数组成为升序的有序数组&#xff0c;第一个值就是最小值&#xff0c;最后一个是最大值 完整代码&#xff1a; 方法一&#xff1a; …

基于Nexus搭建Maven私服基础入门

什么是Nexus&#xff1f;它有什么优势&#xff1f; 要了解为什么需要nexus的存在&#xff0c;我们不妨从以下几个问题来简单了解一下: 为什么需要搭建私服&#xff1f;如果没有私服会出现什么问题&#xff1f; 对于企业开发而言&#xff0c;如果没有私服&#xff0c;我们所有…

自定义日志打印功能--C++

一、介绍 日志是计算机程序中用于记录运行时事件和状态的重要工具。通过记录关键信息和错误情况&#xff0c;日志可以帮助程序开发人员和维护人员追踪程序的执行过程&#xff0c;排查问题和改进性能。 在软件开发中&#xff0c;日志通常记录如下类型的信息&#xff1a; 事件信…

【Flink系列七】TableAPI和FlinkSQL初体验

Apache Flink 有两种关系型 API 来做流批统一处理&#xff1a;Table API 和 SQL Table API 是用于 Scala 和 Java 语言的查询API&#xff0c;它可以用一种非常直观的方式来组合使用选取、过滤、join 等关系型算子。 Flink SQL 是基于 Apache Calcite 来实现的标准 SQL。无论输…

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《基于时空注意力卷积模型的超短期风电功率预测》

这个标题描述了一种用于超短期风电功率预测的模型&#xff0c;该模型基于时空注意力卷积模型。下面我会逐步解读这个标题的关键词和背景&#xff1a; 超短期风电功率预测&#xff1a;风电功率预测是指根据历史风速和其他相关数据&#xff0c;通过建立数学模型来预测未来特定时间…

Windows使用selenium操作浏览器爬虫

以前的大部分程序都是操作Chrome&#xff0c;很少有操作Edge&#xff0c;现在以Edge为例。 Selenium本身是无法直接控制浏览器的&#xff0c;不同的浏览器需要不同的驱动程序&#xff0c;Google Chrome需要安装ChromeDriver、Edge需要安装Microsoft Edge WebDriver&#xff0c…