Mybatis-Plus的SQL注入器实现批量插入/修改,效率比较

这篇具有很好参考价值的文章主要介绍了Mybatis-Plus的SQL注入器实现批量插入/修改,效率比较。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Sql效率

mysql支持一条sql语句插入多条数据。但是Mybatis-Plus中默认提供的saveBatch、updateBatchById方法并不能算是真正的批量语句,而是遍历实体集合执行INSERT_ONE、UPDATE_BY_ID语句。

mybatis-plus虽然做了分批请求、一次提交的处理。但如果jdbc不启用配置rewriteBatchedStatements,那么批量提交的sql到了mysql就还是一条一条执行,mysql并不会将这些sql重写为insert多值插入,相比一条sql批量插入,性能上会差点。

rewriteBatchedStatements文档
https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-connp-props-performance-extensions.html#cj-conn-prop_rewriteBatchedStatements

mybatisplus批量修改,mybatis,sql,mysql

插入10000条数据

  1. mybatis-plus的saveBatch耗时3.6s
##sql格式(不止这些字段)
 <script>
 <foreach collection="list" item="item" separator=";">
 	insert into user (name, age) values ("张三",17)
 </foreach>
 </script>
  1. insert values 耗时1.8s
##sql格式(不止这些字段)
insert into user (name, age) values ("张三", 17), ("李四", 18);

更新10000条数据

  1. mybatis-plus的updateBatchById 耗时3.1s
##sql格式(不止这些字段)
 <script>
 <foreach collection="list" item="item" separator=";">
	update user set name = "张三", age = 17 where id = 1
 </foreach>
</script>
  1. update case when
  • 使用Update case when 是可以一条sql批量更新,一次提交10000条会卡死,得分批处理
  • 最终耗时更久,需要5.8s。
##sql格式(不止这些字段)
update
        user
set
        name=
        (
        case id when 1 then '张三'
        when 2 then '李四'
        else name
end),
        age=
        (
        case id when 1 then 16
        when 2 then 26
        else age
end)
where
        id in ( 1 , 2 )

综合比较效率

10000条数据 mybatis-plus 自定义sql
批量插入 3.6s 1.8s
批量更新 3.1s 5.8s

自定义sql注入器

mybatisplus批量修改,mybatis,sql,mysql文章来源地址https://www.toymoban.com/news/detail-675101.html

1.mybatis-plus给我们提供了自定义sql注入的功能,我们可以自己定义类似insert()、updateById()的方法。

2.首先mybatis-plus在com.baomidou.mybatisplus.extension,给我们提供了4个自带的注入方法。

  1. AlwaysUpdateSomeColumnById 根据Id更新每一个字段,不忽略null字段,数据为null则更新为null。
  2. InsertBatchSomeColumn 单条sql批量插入,通过单SQL的insert语句实现批量插入。
  3. LogicDeleteByIdWithFill 带自动填充的逻辑删除,比如自动填充更新时间、操作人。
  4. Upsert 更新or插入,根据唯一约束判断是执行更新还是删除,相当于提供insert on duplicate key update支持,但mysql应该没有此语法。

3. 类似com.baomidou.mybatisplus.core.enums.SqlMethod里的模板,我们可以继承com.baomidou.mybatisplus.core.injector.AbstractMethod方法来自行定义批量插入/修改的模板方法

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;

public class InsertBatchMethod extends AbstractMethod {
    /**
     * insert into user(id, name, age) values (2, "b", 24), (3, "c", 26);
     <script>
     insert into user(id, name, age) values
     <foreach collection="list" item="item" index="index" open="(" separator="),(" close=")">
     #{item.id}, #{item.name}, #{item.age}
     </foreach>
     </script>
     */
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        final String sql = "<script>insert into %s %s values %s</script>";
        final String fieldSql = prepareFieldSql(tableInfo);
        final String valueSql = prepareValuesSql(tableInfo);
        final String sqlResult = String.format(sql, tableInfo.getTableName(), fieldSql, valueSql);
        SqlSource sqlSource = languageDriver.createSqlSource(
        configuration, sqlResult, modelClass);
        
        //加入Configuration中的Map<String, MappedStatement> mappedStatements,
        //这个mappedStatements保存我们xml中写的各种标签<select>、<insert>、<update>等语句的信息
        //key为全限定类名方法名、value为对应元数据信息
        return this.addInsertMappedStatement(mapperClass, modelClass, "insertBatch", sqlSource, new NoKeyGenerator(), null, null);
    }

    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 prepareValuesSql(TableInfo tableInfo) {
        final StringBuilder valueSql = new StringBuilder();
        valueSql.append("<foreach collection=\"list\" item=\"item\" index=\"index\" open=\"(\" separator=\"),(\" close=\")\">");
        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();
    }
}
public class UpdateBatchMethod extends AbstractMethod {
    /**
      update user set
        name=
        (CASE
                id WHEN 1 THEN '张三'
                WHEN 2 THEN '李四'
        end),
        age =
        (CASE
                id WHEN 1 THEN '2'
                WHEN 2 THEN '2'
        end) where id in (1,2);
	   <script>
	   update user 
	   <trim prefix="set" suffixOverrides=",">
		<trim prefix="name =(case id" suffix="end),">
		  <foreach collection="list" item="item" >
		    <if test="item.name!=null">
		      when #{item.id} then #{item.name}
		    </if>
		  </foreach>
		  else name
		</trim>
		<trim prefix="age =(case id" suffix="end),">
		  <foreach collection="list" item="item" >
		    <if test="item.age!=null">
		      when #{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>
     */
    @Override
    public 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 id\" suffix=\"end),\">\n");
            valueSql.append("<foreach collection=\"list\" item=\"item\" >\n");
            valueSql.append("<if test=\"item.").append(x.getProperty()).append("!=null\">\n");
            valueSql.append("when #{item.id} then #{item.").append(x.getProperty()).append("}\n");
            valueSql.append("</if>\n");
            valueSql.append("</foreach>\n");
            valueSql.append("else ").append(x.getColumn());
            valueSql.append("</trim>\n");
        });
        valueSql.append("</trim>\n");
        return valueSql.toString();
    }
}

4.配置自定义方法SQL注入器

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.extension.injector.methods.InsertBatchSomeColumn;
import org.springframework.context.annotation.Configuration;

import java.util.List;

/**
 * 自定义方法SQL注入器
 */
@Configuration
public class CustomizedSqlInjector extends DefaultSqlInjector {

    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
        List<AbstractMethod> methodList = super.getMethodList(mapperClass);
        methodList.add(new InsertBatchSomeColumn());
        methodList.add(new InsertBatchMethod());
        methodList.add(new UpdateBatchMethod());
        return methodList;
    }
}

5.自定义BaseEntityMapper集成BaseMapper,5. 在BaseEntityMapper中定义对应的抽象方法,方法名要与injectMappedStatement中的addMappedStatement一致

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * 业务实体基础Mapper接口
 */
public interface BaseEntityMapper<T> extends BaseMapper<T> {

    int insertBatch(@Param("list") List<T> list);
    
    Integer insertBatchSomeColumn(List<T> list);
    
    int updateBatch(@Param("list") List<T> list);
}

6. 使用,和Myabtis-plus的insert、updateById等方法一样直接使用即可

import com.test.demo.model.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper extends BaseEntityMapper<User> {
}

...
userMapper.insertBatch(userList);
...

到了这里,关于Mybatis-Plus的SQL注入器实现批量插入/修改,效率比较的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • mybatis-plus 根据指定字段 批量 删除/修改

    mybatis-plus 提供了根据id批量更新和修改的方法,这个大家都不陌生 但是当表没有id的时候怎么办) 这个就不说了,就是因为不想手写SQL 所以才有这篇博客 mybatis plus 的 executeBatch 参考 mybatis plus 的updateBatchById 方法. 调用处: 接口 重写方法 实现 这种写法其实批量的效率还是比较慢的

    2024年02月13日
    浏览(40)
  • Mybatis-Plus如何自定义SQL注入器?

    有关Mybatis-Plus常用功能之前有做过一篇总结: MyBatisPlus常用功能总结!(附项目示例) 我们在使用Mybatis-Plus时,dao层都会去继承BaseMapper接口,这样就可以用BaseMapper接口所有的方法, BaseMapper中每一个方法其实就是一个SQL注入器 在Mybatis-Plus的核心(core)包下,提供的默认可注入方

    2023年04月12日
    浏览(51)
  • Mybatis-Plus 自定义mapper批量新增修改函数

    version MybatisPlusConfig CustomizedSqlInjector InsertBatchMethod UpdateBatchMethod RootMapper 使用方式 mapper接口实现自定义的RootMapper,即可调用批量新增修改函数

    2024年02月12日
    浏览(55)
  • No Spring环境Mybatis-Plus批量插入并返回主键的两种方式

    批量插入,可以把Mybatis-Plus看作是Mybatis加强版;故Mybatis中的相关操作都可以在Mybatis-Plus中使用;在mysql数据库中支持批量插入,所以只要配置useGeneratedKeys和keyProperty就可以批量插入并返回主键了。 下面是批量插入的Dao层接口    一注解方式:      直接撸代码:      第二种: XML方

    2024年02月09日
    浏览(35)
  • Mybatis-Plus批量添加或修改数据的三种方式

    提供的方法 是遍历每一个元素,判断主键是否存在,如果存在则做更新,不存在添加 先获取表中所有的主键 ,然后 判断是否已存在,存在更新,不存在添加 on duplicate key update 是Mysql特有的语法,如下图所示,表中id 为主键 再插入id为1的数据,则提示主键已存在 改成如下

    2024年02月06日
    浏览(45)
  • Mybatis-Plus:实现自定义SQL

    目录 1.简介 2.自定义SQL具体实现 2.1.注解SQL 2.2.Wrapper传参+注解SQL 2.3.Wrapper传参+xml文件SQL 2.4.正常传参+XML文件SQL 3.总结         Mybatis-Plus(以下简称MBP)的初衷是为了简化开发,而不建议开发者自己写SQL语句的;但是有时客户需求比较复杂,仅使用MBP提供的Service,Mapper与

    2024年02月13日
    浏览(43)
  • Mybatis-plus批量操作

            使用Mybatis-plus可以很方便的实现批量新增和批量修改,不仅比自己写foreach遍历方便很多,而且性能也更加优秀。但是Mybatis-plus官方提供的批量修改和批量新增都是根据id来修改的,有时候我们需求其他字段,所以就需要我们自己修改一下。         在Mybatis-plus的IS

    2024年02月11日
    浏览(40)
  • Mybatis-Plus批量更新原理

    IService的updateBatchById方法 默认batchSize = 1000 com.baomidou.mybatisplus.extension.service.impl.ServiceImpl#updateBatchById 构建了一个回调,进入executeBatch方法 在这个方法基本就能看出来了,执行1000次方法后执行一次flushStatements,也就是说理论上是积累了1000个更新sql,才进行一次数据库更新 使用

    2024年02月05日
    浏览(51)
  • mybatis-plus的批量新增insertBatchSomeColumn

    MyBatis-Plus 是基于 MyBatis 进行封装的一套优秀的持久层框架,它提供了丰富的便捷操作方法和强大的代码生成器,大大简化了 MyBatis 的使用。在 MyBatis-Plus 中,我们可以使用 insertBatchSomeColumn 方法来实现批量新增指定字段的操作。 mybatis-plus 的  IService 接口  默认提供   saveBat

    2024年02月01日
    浏览(42)
  • mybatis-plus批量保存异常及效率优化

    最近基于自己公司内部服务维护,发现其中调度中心近期出现不少错误日志,但是该任务却是正常执行,生成的报表数据也是正常的,所以很多天没有发现问题 这就匪夷所思了,    经仔细排查发现,是触发了feign超时hystrix熔断器机制 也就是说子服务出现了执行时间过长的

    2024年01月16日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包