Mybatis自动生成key值(selectKey和useGeneratedKeys)

本文介绍MyBatis框架中使用selectKey和useGeneratedKeys属性进行主键自动生成的方法,包括不同数据库环境下如何配置以支持自增长字段,并讨论了在批量插入场景下这两种策略的应用。

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

insert和update操作中会常常用到自动生成主键的问题。

  1. selectKey和useGeneratedKeys属性 
    useGeneratedKeys (insert and update only) This tells MyBatis to use the JDBC getGeneratedKeys method to retrieve keys generated internally by the database (e.g.auto increment fields in RDBMS like MySQL or SQL Server). Default: false 
    (( 仅 对 insert 和update有 用 ) 这 会 告 诉 MyBatis 使 用 JDBC 的 getGeneratedKeys 方法来取出由数据(比如:像 MySQL 和 SQL Server 这样的数据库管理系统的自动递增字段)内部生成的主键。默认值:false。) 
    keyProperty 
    (insert and update only) Identifies a property into which MyBatis will set the key value returned by 
    getGeneratedKeys , or by a selectKey child element of the insert statement. Default: unset . 
    Can be a comma separated list of property names if multiple generated columns are expected. 
    ((仅对 insert和update有用) 标记一个属性, MyBatis会通过 getGeneratedKeys 或者通过 insert 语句的 selectKey 子元素设置它的值。默认: 不设置。) 
    keyColumn 
    (insert and update only) Sets the name of the column in the table with a generated key. This is only required 
    in certain databases (like PostgreSQL) when the key column is not the first column in the table. Can be a 
    comma separated list of columns names if multiple generated columns are expected.
  2. selectKey和useGeneratedKeys使用
<insert id="insert">
 <selectKey keyProperty="id" resultType="int" order="BEFORE">
  <if test="_databaseId == 'oracle'">
   select seq_users.nextval from dual
  </if>
  <if test="_databaseId == 'db2'">
   select nextval for seq_users from sysibm.sysdummy1"
  </if>
 </selectKey>
 insert into users values (#{id}, #{name})
</insert>
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

通过selectKey在插入操作前或者操作后获取key值,做为字段插入或返回字段。(此段代码获取的序列值id作为字段值插入到users表中)

<insert id="insertAuthor" useGeneratedKeys="true" keyProperty="id">
    insert into Author (username,password,email,bio)
    values (#{username},#{password},#{email},#{bio})
</insert>
 
  • 1
  • 2
  • 3
  • 4

如果数据库支持自增长主键字段(比如mysql、sql server)设置useGeneratedKeys=”true”和keyProperty,这样就可以插入主键id值 
oracle则不支持自增长id,设置useGeneratedKey=”false”,如果设置true则会有报错信息。通过nextval函数,如SEQ_table.Nextval生成id

3.插入更新一条数据时,可以使用selectKey获取id操作。当做多条数据插入更新时,而selectKey只能使用一次,此时应该使用useGeneratedKeys操作。

从Mybatis源码分析selectKey和useGeneratedKeys属性的使用

<think>我们正在解决MyBatis自动生成主键失败的问题。根据引用内容,我们可以从以下几个方面分析原因并提供解决方案: 1. **数据库支持与配置检查**: - 确认数据库是否支持自增主键(如MySQL的AUTO_INCREMENT,PostgreSQL的SERIAL)。如果不支持(如Oracle),则不能使用`useGeneratedKeys`,而需要使用`<selectKey>`标签(引用[2])。 - 在支持自增主键的数据库中,检查表的主键列是否确实设置了自增属性(如MySQL的AUTO_INCREMENT)。 2. **MyBatis配置检查**: - 在Mapper XML文件中,确保`<insert>`标签正确设置了`useGeneratedKeys="true"``keyProperty`属性(如`keyProperty="id"`),且`keyProperty`的与Java对象的主键属性名一致(引用[2])。 - 如果数据库不支持自增主键(如Oracle),需使用`<selectKey>`标签手动生成主键(引用[2]示例)。 3. **代码逻辑检查**: - 插入操作后,是否通过Java对象获取主键(如`user.getId()`),而不是试图从SQL结果中获取(因为插入操作返回的是影响行数,而非主键)。 4. **MyBatis Plus的特殊配置**: - 如果使用MyBatis Plus,可以通过`@TableId`注解指定主键生成策略(引用[1])。例如: ```java public class User { @TableId(type = IdType.AUTO) // 使用数据库自增 private Long id; // 其他属性... } ``` - 注意:`IdType.AUTO`要求数据库主键列设置为自增;`IdType.ASSIGN_ID`(默认)使用雪花算法生成主键(不依赖数据库自增)。 5. **数据库连接配置**: - 检查JDBC URL是否包含参数`useAffectedRows=true`(MySQL),这可能导致主键无法正确回填。建议移除该参数或设置为`false`。 6. **事务与连接问题**: - 确保插入操作在同一个事务中完成,且未提前关闭数据库连接(否则可能无法获取生成的主键)。 ### 解决方案步骤 #### 情况1:使用支持自增主键的数据库(如MySQL) 1. **数据库表检查**:确保主键列设置了`AUTO_INCREMENT`(MySQL)或类似属性。 2. **Mapper XML配置**: ```xml <insert id="insertUser" useGeneratedKeys="true" keyProperty="id"> INSERT INTO user (name, age) VALUES (#{name}, #{age}) </insert> ``` - 注意:SQL中**不包含主键列**(如`id`)。 3. **Java代码**: ```java User user = new User(); user.setName("John"); userMapper.insertUser(user); // 执行插入 System.out.println(user.getId()); // 获取主键 ``` #### 情况2:使用不支持自增主键的数据库(如Oracle) 使用`<selectKey>`标签生成主键(引用[2]示例): ```xml <insert id="addUser" parameterType="User"> <selectKey keyProperty="id" resultType="Integer" order="BEFORE"> SELECT your_sequence.NEXTVAL AS id FROM DUAL </selectKey> INSERT INTO t_user (id, username) VALUES (#{id}, #{username}) </insert> ``` - `order="BEFORE"`:在插入前获取主键(适用于Oracle序列)。 - `keyProperty="id"`:将生成的主键赋给Java对象的`id`属性。 #### 情况3:使用MyBatis Plus 1. 实体类添加注解: ```java public class User { @TableId(type = IdType.AUTO) // 数据库自增 private Long id; // 其他属性... } ``` 2. 如果使用雪花算法(默认`IdType.ASSIGN_ID`),无需数据库自增,但需确保主键类型为`Long`或`String`。 ### 常见错误排查 - **主键未回填**: - 检查`keyProperty`是否拼写错误(如Java对象属性名是`userId`,但配置`keyProperty="id"`)。 - 检查JDBC驱动版本(过低版本可能不支持`getGeneratedKeys`)。 - **Oracle主键重复**: - 确保`<selectKey>`中的序列调用正确(如`your_sequence.NEXTVAL`)。 - **MyBatis Plus主键策略冲突**: - 避免同时使用XML配置`@TableId`注解(优先使用注解配置)。 通过以上步骤,可解决大部分MyBatis自动生成主键失败的问题。若仍无法解决,请检查数据库连接是否正常,以及是否有拦截器修改了SQL或结果。 §§相关问题§§ 1. 如何为Oracle数据库配置MyBatis序列主键生成? 2. MyBatis Plus中`@TableId`注解的常用主键策略有哪些? 3. MyBatis插入后获取主键为null的可能原因有哪些? [^1]: MyBatis-Plus主键生成策略。MyBatis-Plus 主键生成策略中,最常用的是IdType.AUTO,表示主键的生成是由 数据库的自增功能 控制的,前提:数据库的主键列必须设置为 AUTO_INCREMENT。 MyBatis-Plus 提供了内置的主键生成策略,可以在不改动业务代码的前提下自动生成主键,而无需依赖数据库的自增主键。以下是解决方法配置方式: (1)IdType.ASSIGN_ID(默认雪花算法) [^2]: MyBatis插入操作:生成主键。2.如果使用的数据库不支持主键自动增长(如Oracle ),或者支持增长的数据库取消了主键自增的规则时,可以使用MyBatis 提供的另一种方式来自定义生成主键,具体配置示例如下。 <insert id="addUser" parameterType="com.lesour.po.User"> <selectKey keyProperty="id" resultType="Integer" order="BEFORE"> select if(max(id) is null , 1 , max(id) +1) as newld from t customer </selectKey> insert into t_user( id, username , jobs , phone) values(#{id} , #{username} , #{jobs} , #{phone}) </insert> [^3]: 解决mybatis generator生成没有根据主键的select,updatedelete问题。这个是解决另外一个坑的方法 <!-- 只允许生成当前数据库的表 --> <property name="nullCatalogMeansCurrent" value="true"/></think>### MyBatis 自动生成主键失败的原因及解决方案 当 MyBatis 自动生成主键失败时,通常由以下原因导致。请根据具体情况逐步排查: #### 原因一:数据库不支持自增主键(如 Oracle) **现象**:使用 `useGeneratedKeys="true"` 配置无效 **解决方案**:使用 `<selectKey>` 标签手动生成主键 ```xml <insert id="addUser" parameterType="User"> <!-- 针对 Oracle 的序列生成主键 --> <selectKey keyProperty="id" resultType="Long" order="BEFORE"> SELECT your_sequence.NEXTVAL AS id FROM DUAL </selectKey> INSERT INTO t_user(id, username) VALUES(#{id}, #{username}) </insert> ``` **关键参数**: - `order="BEFORE"`:在插入前生成主键(Oracle 常用) - `keyProperty="id"`:指定存储主键的 Java 属性[^2] #### 原因二:数据库自增配置未生效 **现象**:MySQL 等数据库返回的主键为 0 或 null **检查步骤**: 1. 确认数据库表主键已设置 `AUTO_INCREMENT`(MySQL)或 `SERIAL`(PostgreSQL) 2. 验证 JDBC URL 包含参数:`useSSL=false&useUnicode=true&characterEncoding=UTF-8&useAffectedRows=true` 3. 检查数据库用户是否有操作序列的权限(Oracle) #### 原因三:MyBatis 配置错误 **常见错误**: 1. `keyProperty` 名称与实体类属性不匹配 ```xml <!-- 错误示例:Java 属性名为 userId,但配置为 id --> <insert useGeneratedKeys="true" keyProperty="id">...</insert> ``` **修复**:确保 `keyProperty` 等于实体类的主键字段名 2. 忘记在 SQL 中排除主键列 ```sql <!-- 错误示例:包含自增主键列 --> INSERT INTO t_user(id, name) VALUES(#{id}, #{name}) ❌ <!-- 正确写法:省略主键列 --> INSERT INTO t_user(name) VALUES(#{name}) ✅ ``` #### 原因四:MyBatis Plus 注解配置问题 **现象**:使用 `@TableId` 注解但主键未生成 **解决方案**: ```java public class User { // 明确指定主键策略 @TableId(type = IdType.AUTO) // 数据库自增 private Long id; // 或使用雪花算法 // @TableId(type = IdType.ASSIGN_ID) } ``` **主键策略说明**: - `IdType.AUTO`:依赖数据库自增(需数据库支持) - `IdType.ASSIGN_ID`:默认策略,使用雪花算法(不依赖数据库)[^1] #### 原因五:事务或连接问题 **现象**:主键在事务提交前不可用 **解决方案**: ```java // 确保在事务提交后获取主键 User user = new User(); userMapper.insert(user); System.out.println("生成的主键:" + user.getId()); // 立即可用 ``` **注意**:MyBatis 会在执行插入后立即将主键注入对象,无需等待事务提交 #### 终极排查清单 1. ✅ 检查数据库是否支持自增主键 2. ✅ 验证 `keyProperty` 与实体类属性名一致 3. ✅ 确保 SQL 未包含自增主键列 4. ✅ 查看 JDBC 驱动版本(推荐 MySQL Connector/J 8.0+) 5. ✅ 测试数据库连接是否正常 6. ✅ 检查是否有拦截器修改了 SQL 或结果 > **特殊场景处理**:分库分表环境下,建议使用 `IdType.ASSIGN_ID`(雪花算法)避免主键冲突[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值