MyBatis-Plus 主键回填失效问题
作者:程序员牧羊
本文总结了MyBatis-Plus主键回填失效的常见原因及对应解决方法,包括实体类配置、主键字段不匹配、继承冲突、自定义MapperXML、事务回滚等场景,通过详细示例代码与配置建议,帮助开发者有效解决主键回填问题
1. 问题概述
MyBatis-Plus 主键回填失效是开发中常见的问题,主要表现为插入数据后无法获取到数据库自动生成的主键值。本文详细梳理了各种失效场景及对应的解决方案。
2. 主键回填失效的常见情况
2.1 实体类主键字段未正确配置 @TableId 注解
问题描述: 实体类中的主键字段没有使用 @TableId 注解,MyBatis-Plus 无法识别哪个字段是主键。
示例代码:
// 错误示例
public class User {
private Long id; // 没有 @TableId 注解
private String name;
}
// 正确示例
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
}
解决方案: 在主键字段上添加 @TableId 注解,并指定正确的 IdType。
2.2 主键字段名与数据库列名不匹配
问题描述: 实体类主键字段名与数据库主键列名不一致,且未指定 value 属性。
示例代码:
// 错误示例 - 数据库列名是 user_id,实体字段是 id @TableId(type = IdType.AUTO) private Long id; // 数据库列名是 user_id // 正确示例 @TableId(value = "user_id", type = IdType.AUTO) private Long id;
解决方案: 在 @TableId 注解中指定 value 属性,映射到正确的数据库列名。
2.3 继承父类时主键注解冲突
问题描述: 子类继承父类,父类已定义主键注解,子类需要不同的主键字段。
示例代码:
// 父类
public class BaseModel {
@TableId(type = IdType.AUTO)
private Long id; // 父类主键
}
// 子类 - 需要不同的主键字段
public class WarnRule extends BaseModel {
private Long ruleId; // 实际的主键字段
// 其他字段...
}
解决方案:
- 方案一: 在子类中重新定义主键注解
public class WarnRule extends BaseModel {
@TableId(value = "rule_id", type = IdType.AUTO)
private Long ruleId;
// 忽略父类的主键字段
@TableField(exist = false)
private Long id;
}
- 方案二: 使用自定义 Mapper XML 覆盖 insert 方法
<insert id="insert" parameterType="com.example.WarnRule"
useGeneratedKeys="true" keyProperty="ruleId" keyColumn="rule_id">
INSERT INTO warn_rule (dict_id, rule_name, status, create_by, create_time)
VALUES (#{dictId}, #{ruleName}, #{status}, #{createBy}, NOW())
</insert>
2.4 数据库表主键不是自增类型
问题描述: 数据库表主键不是自增类型(AUTO_INCREMENT),但实体类配置了 IdType.AUTO。
示例代码:
// 错误示例 - 数据库主键不是自增 @TableId(type = IdType.AUTO) private Long id; // 但数据库表主键不是 AUTO_INCREMENT
解决方案:
- 修改数据库表结构:
ALTER TABLE your_table MODIFY COLUMN id BIGINT AUTO_INCREMENT PRIMARY KEY;
- 或者使用其他 IdType:
@TableId(type = IdType.INPUT) // 手动输入 private Long id; @TableId(type = IdType.ASSIGN_ID) // 雪花算法 private Long id;
2.5 使用自定义 insert 方法但未配置主键回填
问题描述: 在 Mapper XML 中自定义了 insert 方法,但未配置主键回填。
示例代码:
<!-- 错误示例 - 没有配置主键回填 -->
<insert id="insert" parameterType="com.example.User">
INSERT INTO user (name, age) VALUES (#{name}, #{age})
</insert>
<!-- 正确示例 -->
<insert id="insert" parameterType="com.example.User"
useGeneratedKeys="true" keyProperty="id" keyColumn="id">
INSERT INTO user (name, age) VALUES (#{name}, #{age})
</insert>解决方案: 在自定义 insert 中添加主键回填配置。
2.6 数据库驱动不支持主键回填
问题描述: 某些数据库驱动不支持主键回填功能。
解决方案: 检查并更新数据库驱动版本,或使用其他方式获取主键。
2.7 事务回滚导致主键丢失
问题描述: 在事务中插入数据后,如果事务回滚,主键值可能丢失。
示例代码:
@Transactional
public void saveUser() {
User user = new User();
user.setName("test");
userMapper.insert(user);
// 如果这里抛出异常,事务回滚,user.getId() 可能为 null
System.out.println(user.getId()); // 可能为 null
}
解决方案: 确保在事务提交前使用主键值,或使用编程式事务管理。
3. 完整的解决方案
3.1 标准配置方案
@Entity
@TableName("user")
public class User {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
private String name;
private Integer age;
// getter/setter...
}
3.2 继承场景解决方案
// 父类 - 不定义主键
public class BaseModel {
private String createBy;
private Date createTime;
private String updateBy;
private Date updateTime;
// 其他公共字段...
}
// 子类 - 定义自己的主键
public class WarnRule extends BaseModel {
@TableId(value = "rule_id", type = IdType.AUTO)
private Long ruleId;
private Long dictId;
private String ruleName;
// 其他字段...
}
3.3 自定义 Mapper XML 方案
<insert id="insert" parameterType="com.example.WarnRule"
useGeneratedKeys="true" keyProperty="ruleId" keyColumn="rule_id">
INSERT INTO warn_rule (
dict_id, rule_name, status, create_by, create_time, update_by, update_time
) VALUES (
#{dictId}, #{ruleName}, #{status}, #{createBy}, NOW(), #{updateBy}, NOW()
)
</insert>3.4 编程式主键获取方案
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public Long saveUser(User user) {
// 方案1:使用 MyBatis-Plus 的 insert 方法
userMapper.insert(user);
return user.getId();
// 方案2:使用自定义方法
// userMapper.insertWithKey(user);
// return user.getId();
}
}
4. 调试和验证
4.1 开启 MyBatis-Plus 日志
# application.yml
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl4.2 验证主键回填
@Test
public void testInsertWithKey() {
User user = new User();
user.setName("test");
user.setAge(25);
userMapper.insert(user);
// 验证主键是否回填
assertNotNull(user.getId());
System.out.println("Generated ID: " + user.getId());
}
5. 最佳实践建议
- 统一主键策略: 在项目中统一使用一种主键生成策略
- 避免继承冲突: 谨慎使用继承,避免主键注解冲突
- 测试验证: 在开发过程中及时测试主键回填功能
- 文档记录: 记录项目中的主键配置规则,便于团队协作
- 版本兼容: 注意 MyBatis-Plus 版本升级可能带来的配置变化
1.
6. 常见错误排查清单
- 实体类主键字段是否有
@TableId注解 @TableId注解的value属性是否正确- 数据库表主键是否为自增类型
- 是否使用了自定义 insert 方法
- 自定义 insert 是否配置了主键回填
- 数据库驱动是否支持主键回填
- 是否在事务中正确使用主键值
通过以上详细的梳理和解决方案,应该能够解决大部分 MyBatis-Plus 主键回填失效的问题。
到此这篇关于MyBatis-Plus 主键回填失效问题的文章就介绍到这了,更多相关MyBatis-Plus 主键回填失效内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
