解决mybatisplus saveBatch 或者save 无法插入主键问题

本文详细解析了在使用MyBatisPlus框架时,如何解决自定义主键无法插入的问题。通过修改实体类的@TableId注解,设置type为IdType.INPUT,成功实现了手动赋值主键的插入。

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

解决mybatisplus saveBatch 或者save 无法插入主键问题


通过跟踪源码后得出结论,由于插入的表的主键不是自增的,而是手动赋值的,所以在调用saveBatch 执行的sql语句是没有主键字段的(项目启动后mybatis 会对DynamicSqlSource 中的rootSqlNode对象赋值,里面就定了insert 插入的字段,默认是没有主键字段的 ),所以在设置实体类的主键字段@TableId(value = “xxx”,type = IdType.INPUT)即可,

完整实体类例子:

package com.common.model;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;

import java.io.Serializable;

@Data
@TableName("sys_role_menu")
public class SysRoleMenu implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "role_id",type = IdType.INPUT) //type = IdType.INPUT,没有的话无法插入主键
    private Long roleId;

    @TableField("menu_id")
    private Long menuId;




    @Override
    public String toString() {
        return "SysRoleMenu{" +
        ", roleId=" + roleId +
        ", menuId=" + menuId +
        "}";
    }
}
<think>根据用户的问题,这次明确使用了MyBatis-Plus。在MyBatis-Plus中,插入记录后自动回填主键ID到实体对象中,是默认行为,无需额外配置。我们只需要确保实体类的主键字段使用了`@TableId`注解(如果字段名为id,可以省略,但建议显式标注)。下面详细说明:###MyBatis-Plus插入操作主键回填机制1.**默认行为**:使用`insert`方法后,生成的ID会自动回填到实体对象的主键字段。2.**要求**:-数据库主键需要是自增类型(如MySQL的AUTO_INCREMENT)或者配置了序列(如Oracle)。-实体类的主键字段使用`@TableId`注解,并指定主键策略(如AUTO、INPUT等)。###代码示例```java//实体类@DatapublicclassCkPdfTask{@TableId(type=IdType.AUTO)//使用数据库自增主键privateLongid;privateStringtaskName;//其他字段...}//Mapper接口publicinterfaceCkPdfTaskMapperextendsBaseMapper<CkPdfTask>{//MyBatis-Plus已经提供了基础的insert方法,无需再写SQL}//服务层调用@ServicepublicclassTaskService{@AutowiredprivateCkPdfTaskMapperckPdfTaskMapper;publicvoidcreateTask(){CkPdfTaskckPdfTask=newCkPdfTask();ckPdfTask.setTaskName("PDF生成任务");//执行插入introws=ckPdfTaskMapper.insert(ckPdfTask);//插入后,ckPdfTask的id字段会被自动回填System.out.println("插入记录数:"+rows);System.out.println("生成的主键ID:"+ckPdfTask.getId());}}```###关键点说明1.**`@TableId`注解**:指定主键策略,`IdType.AUTO`表示数据库自增,适用于MySQL等。其他策略包括:-`IdType.NONE`:未设置,需要手动设置主键值。-`IdType.INPUT`:手动输入。-`IdType.ASSIGN_ID`:分配ID(雪花算法)。-`IdType.ASSIGN_UUID`:分配UUID。2.**自动回填原理**:MyBatis-Plus在插入操作后,通过反射将生成的主键值设置到实体对象的主键字段。3.**返回值**:`insert`方法返回的是影响的行数,而主键值已经回填到实体对象中。###注意事项1.**非自增主键**:如果使用如ASSIGN_ID(雪花算法)策略,主键值会在插入之前生成并设置到实体,这样在插入前就已经有ID,不需要数据库返回。2.**批量插入**:MyBatis-Plus的默认批量插入saveBatch)方法,也可以回填主键,但需要数据库支持批量获取主键(例如MySQL的多行插入可以返回最后插入ID,但无法返回全部ID)。因此,对于批量插入,回填主键的功能可能受限(通常只有第一个实体的ID被回填,或者在某些数据库驱动中可能都不回填)。如果需要,可能需要特殊处理。###针对批量插入回填主键解决方案MyBatis-Plus的批量插入默认是不会回填主键的,因为批量插入返回的是受影响的行数,而不是主键。为了解决这个问题,我们可以:1.循环单条插入(性能较低,但能回填)。2.使用支持批量回填的数据库(如SQLServer的批量插入可以返回多个ID),并配合自定义SQL实现。###示例:批量插入并回填主键(MySQL)MySQL的批量插入无法直接获取所有自动生成的主键,但我们可以通过获取第一个生成的主键插入的记录数来推算(因为自增ID是连续的)。但是这种方法并不完全可靠(例如在并发插入时)。另一种方式是使用`insertinto...values(),()...;selectlast_insert_id()`,但是这样只能获取第一个ID。更推荐的方式是在业务中预先生成ID(如使用雪花算法),这样在插入前就已经有ID。```java//使用ASSIGN_ID策略预先生成ID@TableId(type=IdType.ASSIGN_ID)privateLongid;//批量插入前,MyBatis-Plus会自动为每个实体生成IDList<CkPdfTask>tasks=...;ckPdfTaskMapper.insertBatchSomeColumn(tasks);//使用MyBatis-Plus的扩展批量插入方法(需要配置)//此时每个task的id已经被回填(在插入前就已经生成)```注意:`insertBatchSomeColumn`需要配置自定义SQL注入器,详情参考MyBatis-Plus官方文档。###结论在MyBatis-Plus中,插入操作后会自动将主键回填到实体对象中(只要正确配置主键策略)。对于自增主键,在插入后回填;对于分配策略(如ASSIGN_ID),在插入前就已经赋值。§§相关问题§§:1.MyBatis-Plus中如何配置主键策略(如使用雪花算法)?2.在批量插入时,MyBatis-Plus如何保证主键回填?3.使用Oracle的序列时,MyBatis-Plus的主键策略应该如何配置?4.如果字段名不是idMyBatis-Plus如何指定主键字段?</think> 在 MyBatis-Plus 中,插入操作后的主键回填机制处理得更加简洁高效。以下是详细解答: ### MyBatis-Plus 的主键回填机制 在 MyBatis-Plus 中,当你调用 `insert` 方法后,**生成的 ID 会自动填充到实体对象的主键字段中**。这是 MyBatis-Plus 的默认行为,无需额外配置。以下是具体实现细节: ```java // 实体类示例(需添加注解) @Data @TableName("ck_pdf_task") public class CkPdfTask { @TableId(type = IdType.AUTO) // 关键注解:指定主键生成策略 private Long id; private String taskName; // 其他字段... } // 服务层调用示例 @Service public class PdfTaskService { @Autowired private CkPdfTaskMapper ckPdfTaskMapper; public void createTask() { CkPdfTask task = new CkPdfTask(); task.setTaskName("PDF生成任务"); // 执行插入操作 int result = ckPdfTaskMapper.insert(task); // 此时ID已自动回填到task对象中 System.out.println("生成的主键ID: " + task.getId()); System.out.println("影响行数: " + result); } } ``` ### 关键注解说明 1. **`@TableId` 注解**: - `type = IdType.AUTO`:使用数据库自增ID(MySQL的AUTO_INCREMENT) - 其他选项: - `NONE`:未设置主键 - `INPUT`:手动输入 - `ASSIGN_ID`:分配ID(雪花算法) - `ASSIGN_UUID`:分配UUID 2. **支持的主键策略**: ```java public enum IdType { AUTO(0), // 数据库自增 NONE(1), // 无状态 INPUT(2), // 手动输入 ASSIGN_ID(3), // 分配ID(默认雪花算法) ASSIGN_UUID(4); // 分配UUID } ``` ### 特殊场景处理 **1. 批量插入的回填:** ```java List<CkPdfTask> tasks = new ArrayList<>(); // 添加多个任务对象... // 批量插入(注意:需要3.5.0+版本) boolean success = ckPdfTaskMapper.insertBatchSomeColumn(tasks); // 所有任务对象的主键已回填 tasks.forEach(task -> System.out.println(task.getId())); ``` **2. 非自增主键(雪花算法):** ```java @Data public class CkPdfTask { @TableId(type = IdType.ASSIGN_ID) // 使用分布式ID private Long id; // 其他字段... } // 调用时ID会在插入前生成并设置 CkPdfTask task = new CkPdfTask(); ckPdfTaskMapper.insert(task); System.out.println("分布式ID: " + task.getId()); // 输出类似1258337759825928194 ``` ### 工作原理 1. 执行前:MyBatis-Plus 会检查主键策略 2. 对于 `IdType.AUTO`: - 执行 SQL:`INSERT INTO ck_pdf_task(...) VALUES (...)` - 获取数据库生成的自增 ID - 通过反射将 ID 设置到实体对象 3. 对于 `IdType.ASSIGN_ID`: - 在插入**前**生成分布式 ID - 直接设置到实体对象 - 执行带 ID 的 INSERT 语句
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

_令狐大侠_

觉的文章对你有用,鼓励一下吧

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

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

打赏作者

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

抵扣说明:

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

余额充值