MyBatis用HashMap动态插入后获取Oracle自增主键的问题
解决方案:Oracle下动态获取多表插入的自增主键
针对Oracle数据库中多表动态插入时无法配置动态keyColumn的问题,以下是三种可行方案:
方案一:使用Oracle RETURNING 子句(推荐)
Oracle支持通过RETURNING子句直接返回插入生成的主键值,无需依赖MyBatis的useGeneratedKeys,可完美适配动态主键列名。
修改XML映射文件如下:
<insert id="insert" parameterType="de.gwvs.btix.database.model.GenericRecordMap"> INSERT INTO ${genericRecordMap.tableName} <foreach open="(" close=")" collection="genericRecordMap.tableMap" index="col" separator=","> ${col} </foreach> VALUES <foreach open="(" close=")" collection="genericRecordMap.tableMap" item="val" separator=","> #{val} </foreach> -- 动态指定主键列,将生成的值赋值给GenericRecordMap的id属性 RETURNING ${genericRecordMap.pk} INTO #{id,jdbcType=BIGINT,mode=OUT} </insert>
注意事项:
- 确保传入的
GenericRecordMap对象的pk字段正确填写目标表的主键列名(如USER_ID、ORDER_ID) - 根据主键实际数据类型调整
jdbcType参数(例如字符串类型用VARCHAR)
执行插入后,主键值会自动填充到传入的GenericRecordMap对象的id字段中。
方案二:使用序列CURRVAL(适用于序列生成主键的场景)
如果表主键是通过序列生成的(插入时使用序列名.NEXTVAL赋值),可通过<selectKey>动态查询序列当前值。
步骤1:构造插入参数时指定序列值
GenericRecordMap record = GenericRecordMap.builder() .tableName("USER") .pk("USER_ID") .tableMap(new HashMap<String, Object>() {{ put("USER_ID", "USER_ID_SEQ.NEXTVAL"); // 直接使用序列生成主键 put("USER_NAME", "test_user"); }}) .build(); recordMapper.insert(record);
步骤2:修改XML映射文件
<insert id="insert" parameterType="de.gwvs.btix.database.model.GenericRecordMap"> <!-- 插入后查询序列当前值,赋值给id属性 --> <selectKey keyProperty="id" resultType="long" order="AFTER"> SELECT ${genericRecordMap.pk}_SEQ.CURRVAL FROM DUAL </selectKey> INSERT INTO ${genericRecordMap.tableName} <foreach open="(" close=")" collection="genericRecordMap.tableMap" index="col" separator=","> ${col} </foreach> VALUES <foreach open="(" close=")" collection="genericRecordMap.tableMap" item="val" separator=","> ${val} </foreach> </insert>
说明:如果序列命名无固定规则,可在GenericRecordMap中新增seqName字段存储序列名,将<selectKey>中的SQL改为SELECT ${genericRecordMap.seqName}.CURRVAL FROM DUAL。
方案三:自定义MyBatis插件(进阶)
若必须使用useGeneratedKeys,可通过自定义插件拦截插入操作,动态设置keyColumn的值。
1. 编写插件类
import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.plugin.*; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.reflection.SystemMetaObject; import java.sql.Connection; import java.util.Properties; @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})}) public class DynamicKeyColumnPlugin implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); MetaObject metaObject = SystemMetaObject.forObject(statementHandler); MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement"); // 仅处理目标insert方法 if ("insert".equals(mappedStatement.getId()) && mappedStatement.getKeyProperties() != null) { Object parameterObject = metaObject.getValue("delegate.parameterHandler.parameterObject"); if (parameterObject instanceof GenericRecordMap) { GenericRecordMap recordMap = (GenericRecordMap) parameterObject; // 动态设置keyColumn为传入的主键列名 metaObject.setValue("delegate.mappedStatement.keyColumns", recordMap.getPk()); } } return invocation.proceed(); } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) {} }
2. 注册插件
在MyBatis配置文件中添加插件:
<plugins> <plugin interceptor="de.gwvs.btix.database.plugin.DynamicKeyColumnPlugin"/> </plugins>
3. 修改insert标签
保留useGeneratedKeys配置,移除keyColumn:
<insert id="insert" parameterType="de.gwvs.btix.database.model.GenericRecordMap" useGeneratedKeys="true" keyProperty="id"> INSERT INTO ${genericRecordMap.tableName} <foreach open="(" close=")" collection="genericRecordMap.tableMap" index="col" separator=","> ${col} </foreach> VALUES <foreach open="(" close=")" collection="genericRecordMap.tableMap" item="val" separator=","> #{val} </foreach> </insert>
内容的提问来源于stack exchange,提问作者GMarjano
相关产品推荐
相关产品推荐

