Mybatis-plus两种批量插入方法比较(带源码)

本文对比分析了Mybatis-plus中使用SQL注入器和IService方式进行批量插入的性能差异。测试结果显示,一万条数据时两者相差4倍左右,但当数据量增大到一百万条时,IService执行时间超过2分钟,而SQL注入器效率更高。原因在于IService方式底层采用迭代器逐条插入,而SQL注入器通过拼接SQL一次性执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 SQL注入器方式

1.1 SqlInjector 注入器类

package com.example.demo.config.mybatis;

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.extension.injector.methods.InsertBatchSomeColumn;

import java.util.List;

/**
 * @author liyue
 * @date 2021/12/8 15:32
 */
public class SqlInjector extends DefaultSqlInjector {
   
   

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

1.2 MyBatisPlusConfig 配置类

package com.example.demo.config.mybatis;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

/**
 * @author liyue
 * @date 2021/12/8 15:31
 */
@EnableTransactionManagement
@Configuration
@MapperScan("com.example.demo.mapper")
public class MyBatisPlusConfig {
   
   

    @Bean
    public SqlInjector sqlInjector(){
   
   
        return new SqlInjector();
    }
}

1.3 Mapper基础类

package com.example.demo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;

import java.util.List;

/**
 * @author liyue
 * @date 2021/12/8 15:35
 */
public interface MyBaseMapper<T> extends BaseMapper<T> {
   
   

    /**
     * 批量插入
     * @param entityList
     * @return
     */
    Integer insertBatchSomeColumn(List<T> entityList);
}

1.4 UserMapper 继承MyBaseMapper类

package com.example.demo.mapper;

import com.example.demo.entity.User;

/**
 * @author liyue
 * @date 2021/12/6 15:32
 */
public interface UserMapper extends MyBaseMapper<User> {
   
   
}

2 IService方式

2.1 BatchHandlerMapper 接口

BatchHandlerMapper 接口继承IService

package com.example.demo.mapper;

import com.baomidou.mybatisplus.extension.service.IService;
import com.example.demo.entity.Dept;

/**
 * @author liyue
 * @date 2021/12/8 16:42
 */
public interface BatchHandlerMapper extends IService<Dept
<think>我们讨论的是在MyBatis-Plus批量插入时如何避免重复数据。根据引用内容,我们可以结合去重和批量插入的技术。核心思路:1.在插入之前,在应用层(Java代码)中对数据进行去重处理。2.在数据库层面设置唯一约束(如唯一索引),利用数据库的特性避免重复(当重复时,可以选择忽略或更新)。具体步骤:方法一:应用层去重(推荐)-步骤1:在将数据列表传递给MyBatis-Plus批量插入方法之前,先对列表进行去重。-步骤2:去重方法可以使用之前提到的Streamdistinct()或LinkedHashSet,但前提是对象必须正确重写equals()和hashCode()方法(根据业务唯一标识)。-步骤3:然后使用MyBatis-Plus批量插入方法(例如saveBatch)插入去重后的列表。方法二:数据库层去重(利用唯一索引和批量插入的SQL特性)-步骤1:在数据库表中为需要唯一约束的字段(或字段组合)创建唯一索引。-步骤2:在批量插入时,使用MyBatis-Plus提供的自定义批量插入方法(如InsertBatchSomeColumn)并配合ONDUPLICATEKEYUPDATE(MySQL)或INSERT...ONCONFLICT...DOUPDATE(PostgreSQL)等语句,实现重复时更新或忽略。根据引用[3],我们可以使用XML映射语句实现批量插入并处理重复数据(使用ONDUPLICATEKEYUPDATE)。但是注意,MyBatis-Plus默认的saveBatch方法不支持该特性,需要自定义SQL。方法三:使用MyBatis-Plus的扩展(InsertBatchSomeColumn方法)-引用[5]提到,通过扩展MyBatis-Plus的SqlInjector,添加InsertBatchSomeColumn方法,该方法支持批量插入,并且可以配置插入的字段(排除某些字段,如更新字段)。-但是,该方法本身并不处理重复数据,因此需要结合方法二(数据库唯一索引)以及扩展SQL语句(使用ONDUPLICATEKEYUPDATE)来实现重复数据的处理。具体实现:方案1:应用层去重+批量插入```java//假设有一个原始数据列表List<YourEntity>list=...;//去重(使用LinkedHashSet保持顺序)Set<YourEntity>set=newLinkedHashSet<>(list);List<YourEntity>distinctList=newArrayList<>(set);//使用MyBatis-Plus的saveBatch方法yourService.saveBatch(distinctList);```方案2:数据库唯一索引+自定义批量插入SQL(处理重复)-步骤1:在数据库表中创建唯一索引(例如,在字段name上创建唯一索引)。-步骤2:编写自定义的Mapper方法,使用ONDUPLICATEKEYUPDATE(MySQL)语法。Mapper接口:```javapublicinterfaceYourMapperextendsBaseMapper<YourEntity>{intinsertBatchOnDuplicateUpdate(List<YourEntity>list);}```XML映射文件:```xml<insertid="insertBatchOnDuplicateUpdate">INSERTINTOyour_table(id,name,...)VALUES<foreachcollection="list"item="item"separator=",">(#{item.id},#{item.name},...)</foreach>ONDUPLICATEKEYUPDATEname=VALUES(name),...=VALUES(...)</insert>```注意:ONDUPLICATEKEYUPDATE后面的字段更新可以根据业务需求设置。如果只是不想插入重复数据(忽略重复),可以使用IGNORE关键字(但要注意IGNORE会忽略所有错误,谨慎使用)或者使用ONDUPLICATEKEYUPDATE更新为原值(但这样还是会触发更新操作,可能影响自增主键)。方案3:使用MyBatis-Plus扩展的InsertBatchSomeColumn方法,并配合ONDUPLICATEKEYUPDATE(需要进一步自定义)-引用[5]中提供的扩展方法(InsertBatchSomeColumn)只实现了批量插入,没有处理重复。所以需要在此基础上修改SQL,添加ONDUPLICATEKEYUPDATE部分,这可能需要修改InsertBatchSomeColumn的源码(或自定义一个方法)。性能考虑:-引用[2]提到,InsertBatchSomeColumn的性能优于saveBatch(因为saveBatch实际上是循环单条插入,而InsertBatchSomeColumn是真正的批量SQL)。-因此,在数据量较大时,建议使用方案2(自定义批量插入SQL)或方案3(扩展InsertBatchSomeColumn并支持重复处理)。注意事项:-应用层去重只能保证本次插入的列表中没有重复,但无法避免与数据库中已有数据的重复。因此,如果要去除与数据库已有数据的重复,必须在数据库层设置唯一约束,并处理插入时的重复情况。-数据库唯一索引是保证数据唯一性的最终手段。总结:-如果重复只存在于待插入的列表中,使用应用层去重(方案1)即可。-如果还需要避免与数据库中已有数据重复,则必须使用方案2(数据库唯一索引+自定义SQL处理重复插入)。根据引用[4]中的异常,注意在扩展MyBatis-Plus方法时,要正确配置Mapper和SQL映射,避免出现找不到Mapper方法的异常。相关问题:1.MyBatis-Plus中如何配置唯一索引?2.如何扩展MyBatis-Plus批量插入方法?3.ONDUPLICATEKEYUPDATE和REPLACEINTO有什么区别?4.如何在SpringBoot中正确重写实体类的equals和hashCode方法?5.批量插入时如何同时返回自增主键?</think>### MyBatis-Plus 批量插入数据避免重复的解决方案 在 MyBatis-Plus 批量插入时避免重复数据,需要结合应用层去重和数据库层约束两种策略。以下是具体实现方案: --- #### 方案一:应用层去重(推荐) **在插入前对 Java 集合进行去重处理** ```java // 1. 实体类必须正确重写equals和hashCode public class User { @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; return Objects.equals(uniqueField, user.uniqueField); // 根据业务唯一标识 } @Override public int hashCode() { return Objects.hash(uniqueField); } } // 2. 插入前使用Stream去重 List<User> users = ... // 原始数据 List<User> distinctUsers = users.stream() .distinct() .collect(Collectors.toList()); // 3. 执行批量插入 userService.saveBatch(distinctUsers); // 使用MyBatis-Plus的saveBatch方法 ``` **优点**: - 减少数据库压力 - 避免无效的插入操作 - 保持业务逻辑清晰[^1] --- #### 方案二:数据库层去重 **通过数据库唯一约束确保数据唯一性** ```sql -- 创建唯一索引(MySQL示例) ALTER TABLE user ADD UNIQUE INDEX idx_unique_field (unique_field); ``` **批量插入时处理重复数据**(XML方式): ```xml <insert id="batchInsertOnDuplicateUpdate"> INSERT INTO user (id, name, unique_field) VALUES <foreach collection="list" item="item" separator=","> (#{item.id}, #{item.name}, #{item.uniqueField}) </foreach> ON DUPLICATE KEY UPDATE name = VALUES(name) -- 重复时更新其他字段 </insert> ``` **处理策略**: | 场景 | SQL 方案 | |--------------------------|----------------------------------| | 忽略重复 | `INSERT IGNORE INTO ...` | | 重复时更新其他字段 | `ON DUPLICATE KEY UPDATE ...` | | 重复时不做任何操作 | 使用唯一索引 + 应用层捕获异常 | --- #### 方案三:MyBatis-Plus 扩展方法 **使用高性能的 `InsertBatchSomeColumn` 方法**(需配置扩展)[^5]: ```java // 1. 自定义SQL注入器 public class CustomSqlInjector extends DefaultSqlInjector { @Override public List<AbstractMethod> getMethodList(Class<?> mapperClass) { List<AbstractMethod> methods = super.getMethodList(mapperClass); methods.add(new InsertBatchSomeColumn(i -> !i.isLogicDelete())); return methods; } } // 2. 配置类 @Configuration public class MybatisPlusConfig { @Bean public CustomSqlInjector sqlInjector() { return new CustomSqlInjector(); } } // 3. Mapper接口添加方法 public interface UserMapper extends BaseMapper<User> { Integer insertBatchSomeColumn(@Param("list") List<User> list); } // 4. 使用去重后的数据调用 userMapper.insertBatchSomeColumn(distinctUsers); ``` **性能对比**(引用[2]测试数据): ``` InsertBatchSomeColumn > 自定义XML > SaveBatch > 循环插入 ``` --- #### 最佳实践组合 1. **前端/服务层**:提交数据前进行初步去重 2. **Java 应用层**: - 使用 `stream().distinct()` 去重 - 调用高性能的 `insertBatchSomeColumn` 方法 3. **数据库层**: - 设置唯一索引作为最终保障 - 使用 `ON DUPLICATE KEY UPDATE` 处理重复 --- #### 常见问题解决 **问题:`PersistenceException: Mapped Statements not found`** **原因**:未正确配置自定义方法[^4] **解决**: 1. 检查 Mapper 接口是否声明自定义方法 2. 确认 XML 文件中 SQL 的 id 与方法名匹配 3. 验证是否注入 `CustomSqlInjector` ```java // 正确配置示例 @Mapper public interface UserMapper extends BaseMapper<User> { // 必须声明自定义方法 Integer insertBatchSomeColumn(@Param("list") List<User> list); } ``` --- #### 相关问题 1. 如何在 MyBatis-Plus 中配置多字段组合唯一索引?[^3] 2. `ON DUPLICATE KEY UPDATE` 和 `REPLACE INTO` 的性能差异是什么?[^2] 3. 批量插入时如何获取数据库生成的自增ID?[^5] 4. MyBatis-Plus 批量更新时如何避免重复更新? 5. 高并发场景下如何保证批量插入的数据唯一性?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值