Mybatis结合Oracle的foreach insert批量插入报错!

        最近做一个批量导入的需求,将多条记录批量插入数据库中。解决思路:在程序中封装一个List集合对象,然后把该集合中的实体插入到数据库中,因为项目使用了MyBatis,所以打算使用MyBatis的foreach功能进行批量插入。期间遇到了“SQL 命令未正确结束 ”的错误,最终解决,记录下来供以后查阅和学习。

        首先,在网上参考了有关Mybatis的foreach insert的资料,具体如下:

        foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合。

        foreach元素的属性主要有 item,index,collection,open,separator,close。

        item表示集合中每一个元素进行迭代时的别名,index指定一个名字,用于表示在迭代过程中,每次迭代到的位置,open表示该语句以什么开始,separator表示在每次进行迭代之间以什么符号作为分隔符,close表示以什么结束,在使用foreach的时候最关键的也是最容易出错的就是collection属性,该属性是必须指定的,但是在不同情况 下,该属性的值是不一样的,主要有一下3种情况:

        1.如果传入的是单参数且参数类型是一个List的时候,collection属性值为list

        2.如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array

        3.如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map

        然后,照葫芦画瓢写了如下的xml文件,

xxxMapper.xml文件:

<insert id="addSupCity" parameterType="java.util.List">
    <selectKey keyProperty="cityId" order="BEFORE" resultType="String">
        <![CDATA[SELECT SEQ_OCL_SUPCITY.NEXTVAL FROM dual]]>
    </selectKey>
    INSERT INTO T_OCL_SUPCITY
    (CITY_ID,CITY_CODE, CITY_NAME, AREA_DESC, SUP_ID, STAT)
    VALUES 
    <foreach collection="list" item="item" index="index" separator=",">     
      (
        #{item.cityId,jdbcType=VARCHAR},
        #{item.cityCode,jdbcType=VARCHAR},
        #{item.cityName,jdbcType=VARCHAR},
        #{item.areaDesc,jdbcType=VARCHAR},
        #{item.supId,jdbcType=VARCHAR},
        #{item.stat,jdbcType=VARCHAR}
      )
    </foreach>
</insert>

        但是运行起来后就一直报错,报错信息如下:

### SQL: INSERT INTO T_OCL_SUPCITY
(CITY_ID,CITY_CODE, CITY_NAME, AREA_DESC, SUP_ID, STAT) VALUES (?,?,?,?,?),(?,?,?,?,?)
### Cause: java.sql.SQLSyntaxErrorException: ORA-00933: SQL 命令未正确结束

        把SQL复制出来在PL/SQL中运行也是报同样的错,如上也可以看出,使用批量插入执行的SQL语句等价于: INSERT INTO T_OCL_SUPCITY (CITY_ID,CITY_CODE, CITY_NAME, AREA_DESC, SUP_ID, STAT) VALUES (?,?,?,?,?),(?,?,?,?,?),而在oracle中用insert into xxx values (xxx,xxx),(xxx,xxx) 这种语法是通不过的 。再回过头去看那篇文章,发现这是适用于MySQL的,不适用于Oracle,因此把xml文件修改一下:

<insert id="addSupCity" parameterType="java.util.List">
      INSERT INTO T_OCL_SUPCITY
  (CITY_ID,CITY_CODE, CITY_NAME, AREA_DESC, SUP_ID, STAT)
SELECT SEQ_OCL_SUPCITY.NEXTVAL CITY_ID, A.*
FROM(
<foreach collection="list" item="item" index="index" separator="UNION ALL">
 SELECT 
       #{item.cityCode,jdbcType=VARCHAR} CITY_CODE,
       #{item.cityName,jdbcType=VARCHAR} CITY_NAME,
       #{item.areaDesc,jdbcType=VARCHAR} AREA_DESC,
       #{item.supId,jdbcType=VARCHAR} SUP_ID,
       #{item.stat,jdbcType=VARCHAR} STAT
     FROM dual
   </foreach>
   )A
  </insert>

        运行通过。在Oracle的版本中,有几点需要注意的:

        1.SQL中没有VALUES;

        2.<foreach>标签中的(selece ….. from dual);

        3.<foreach>标签中的separator的属性为”UNION ALL”,将查询合并结果集。

作者:GreatQing

<think>嗯,用户在使用MyBatis-Plus进行XML批量插入时遇到了缺少主键导致的报错问题。首先,我需要了解MyBatis-Plus的默认主键生成策略。根据文档,MyBatis-Plus默认使用雪花算法生成主键,这个策略对应的是`IdType.ASSIGN_ID`。所以如果实体类的主键字段没有正确配置这个策略,或者数据库表的主键不是自增的,就会导致插入时主键为null,从而报错。 接下来,用户可能的情况是主键字段没有设置`@TableId`注解,或者设置的类型不对。比如,如果数据库使用的是自增主键,而实体类配置的是`ASSIGN_ID`,这时候MyBatis-Plus不会自动生成主键值,导致插入时主键为null。这时候需要检查实体类的注解是否正确,可能需要改为`IdType.AUTO`。 另外,数据库表的主键是否允许为null也很重要。如果数据库设计时主键不允许null,而插入时没有提供值,自然会报错。需要确认表结构,比如主键是否设置为自增,或者是否有默认值。例如,MySQL的自增主键需要设置`AUTO_INCREMENT`属性,这样即使插入时不提供主键值,数据库也会自动生成。 然后,XML中的批量插入语句是否正确。在MyBatisforeach循环中,是否包含了主键字段。如果主键需要数据库自增生成,那么插入语句中不应该包含主键列,或者在插入时显式设置为null,并依赖数据库的自增功能。这时候需要检查SQL语句,确保主键列的处理正确。 还有,全局配置是否正确。在MyBatis-Plus的配置文件中,是否设置了正确的主键生成策略。例如,在`application.yml`中配置`id-type: assign_id`或`auto`,根据数据库的实际需求来调整。 可能的解决方案包括:1. 检查并修正实体类的主键注解,确保使用正确的`IdType`。2. 调整数据库表结构,设置主键为自增或允许null(不过主键通常不允许null)。3. 修改XML中的插入语句,排除主键字段或正确处理主键生成。4. 在插入前手动生成主键值,比如使用UUID或者自定义的ID生成器。 此外,参考引用中的例子,比如引用[2]提到插入时允许字段为NULL,但主键通常不能为NULL,所以可能需要确保数据库主键自增或者在插入时生成主键值。引用[3]提到野指针指向0地址会触发错误,但这里的问题类似,主键为null导致数据库拒绝插入,所以需要确保主键有有效的值。 总结下来,用户需要从实体类配置、数据库表结构、XML语句以及全局配置几个方面排查,确保主键在插入时能正确生成或由数据库自动处理。</think>### MyBatis-Plus XML批量插入主键缺失问题解决方案 #### 1. 检查主键生成策略 MyBatis-Plus默认使用雪花算法生成主键(对应$IdType.ASSIGN_ID$)。若实体类主键字段未正确配置,会导致插入时主键为null。需确保实体类主键字段添加注解: ```java @TableId(type = IdType.ASSIGN_ID) // 雪花算法 // 或 @TableId(type = IdType.AUTO) // 数据库自增主键 ``` #### 2. 数据库表结构调整 确认数据库表主键是否允许自增: ```sql ALTER TABLE your_table MODIFY id BIGINT AUTO_INCREMENT; -- MySQL示例 ``` 若使用Oracle需创建序列: ```sql CREATE SEQUENCE seq_name INCREMENT BY 1; ``` #### 3. XML映射文件优化 在批量插入SQL中明确处理主键字段: ```xml <insert id="batchInsert"> INSERT INTO your_table <if test="list[0].id != null">(id, col1, col2)</if> <if test="list[0].id == null">(col1, col2)</if> VALUES <foreach collection="list" item="item" separator=","> <if test="item.id != null">(#{item.id}, #{item.col1}, #{item.col2})</if> <if test="item.id == null">(DEFAULT, #{item.col1}, #{item.col2})</if> </foreach> </insert> ``` #### 4. 全局配置验证 在`application.yml`中设置: ```yaml mybatis-plus: global-config: db-config: id-type: assign_id # 根据实际策略调整 ``` #### 5. 异常场景处理 当使用数据库自增主键时,需保证: - 实体类主键字段标记为$@TableId(type = IdType.AUTO)$ - JDBC连接参数添加`useGeneratedKeys=true` - 禁止在插入语句中包含主键字段 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值