mybatis的批量新增

开发项目中,总是与数据打交道,有的时候将数据放入到一个集合中,然后在遍历集合一条一条的插入,感觉效率超不好,最近又碰到这个问题,插入50条数据用了将近1s,完全满足不了系统的需求.效率必须加快,然后网上查询资料,历经千万bug,终于搞定,这里指提供mybatis中的配置,至于dao层的调用mybatis就自己上网查询下资料吧

1根据网上搜了一下资料,在sql-mapper.xml文件中写了如下配置可进行批量操作

< insert id ="insertBatch" parameterType="List" > 
    insert into REAL_DATA_HW(
 M_LINE_NO,M_TIME,HW_NUM, VOL_A,VOL_B,VOL_C )
    values 
    < foreach collection ="list" item ="item" index ="index" separator =","> 
       (
 #{obj.M_LINE_NO},to_date(#{obj.M_TIME},'yyyy-MM-dd hh24:mi:ss'),#{obj.HW_NUM},
       #{obj.VOL_A},#{obj.VOL_B},#{obj.VOL_C} ) 
    </ foreach > 
</ insert >

然后启动,调用这个操作的时候报错 
Error setting null parameter. Most JDBC driversrequire that the JdbcType must be specified for all nullable parameters. Cause:java.sql.SQLException: 无效的列类型 
上网继续查询说加上 <![CDATA[    ]]> 即可,好吧配置变成了 

< insert id ="insertBatch" parameterType="List" > 
<![CDATA[
    insert into REAL_DATA_HW(
 M_LINE_NO,M_TIME,HW_NUM, VOL_A,VOL_B,VOL_C )
    values 
]]>
    < foreach collection ="list" item ="item" index ="index" separator =","> 
       (
 #{obj.M_LINE_NO},to_date(#{obj.M_TIME},'yyyy-MM-dd hh24:mi:ss'),#{obj.HW_NUM},
       #{obj.VOL_A},#{obj.VOL_B},#{obj.VOL_C} ) 
    </ foreach > 
</ insert >

然后依然报同样的错,查来查去,发现一句坑爹的话这个只是支持mySQL,去年买了个表。。。 
继续找方法吧,又被我找到一种在ORACLE中可以用的方法,参考网上的步骤,有了如下的配置

< insert id ="insertBatch" parameterType="List" > 
    insert into REAL_DATA_HW(
 M_LINE_NO,M_TIME,HW_NUM, VOL_A,VOL_B,VOL_C )
    < foreach collection ="list" item ="item" index ="index" separator ="union all"> 
     select   
 #{obj.M_LINE_NO},to_date(#{obj.M_TIME},'yyyy-MM-dd hh24:mi:ss'),#{obj.HW_NUM},
       #{obj.VOL_A},#{obj.VOL_B},#{obj.VOL_C} from dual 
    </ foreach > 
</ insert >

这个的原理应该是参考的insert into  tableName select 。。。 from tableName1 这个方式
重新启动,然后继续报错,奔溃

org.springframework.jdbc.UncategorizedSQLException: Error setting null parameter. Most JDBC drivers require that the JdbcType must be specified for all nullable parameters...哎 ,找原因,mybaties对null缺乏处理,需要在字段后加上jdbcType=类型,于是添加类型吧,顺带去网上查询了mybatis的类型
iBatis官方的说法是只要是JDBC提供的JdbcType类中所定义的常量字符串,jdbcType这个属性就可以取这个值,虽然有一些类型iBatis尚且不能支持(例如blobs)。而JdbcType类则由不同的JDBC Driver提供,可能由于Driver(不同类型的数据库有不同的Driver)的不同会存在差异,不过大同小异。一般都支持如下类型(大小写不敏感):
Array, BigInt, Binary, Bit, Blob, Boolean, Char, Clob, Datalink, Date, Decimal,
 Double, Float, Integer, LongVarBinary, LongVarChar, Numeric, Real, Ref, SmallInt, Struct, Time, Timestamp, TinyInt, VarBinary, VarChar.
大小写不敏感,,配置如下

< insert id ="insertBatch" parameterType="List" > 
    insert into REAL_DATA_HW(
 M_LINE_NO,M_TIME,HW_NUM, VOL_A,VOL_B,VOL_C )
    < foreach collection ="list" item ="item" index ="index" separator ="union all"> 
     select   
 #{obj.M_LINE_NO,jdbcType= VarChar },to_date(#{obj.M_TIME ,jdbcType= VarChar },'yyyy-MM-dd hh24:mi:ss'),#{obj.HW_NUM , jdbcType=Double },
       #{obj.VOL_A ,jdbcType= Double },#{obj.VOL_B ,jdbcType= Double },#{obj.VOL_C ,jdbcType= Double } from dual
    </ foreach > 
</ insert >

调用的时候继续报错,有一个是
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.builder.BuilderException: Error resolving JdbcType. Cause: java.lang.IllegalArgumentException: No enum const class org.apache.ibatis.type.JdbcType.Double
亲,不敏感你妹!!!!!!!!!
于是慢慢替换了下Double,终于在Double写成DOUBLE的时候不在报这个错误了,此时配置如下

< insert id ="insertBatch" parameterType="List" > 
    insert into REAL_DATA_HW(
 M_LINE_NO,M_TIME,HW_NUM, VOL_A,VOL_B,VOL_C )
    < foreach collection ="list" item ="item" index ="index" separator ="union all"> 
     select   
 #{obj.M_LINE_NO,jdbcType= VARCHAR },to_date(#{obj.M_TIME ,jdbcType= VARCHAR },'yyyy-MM-dd hh24:mi:ss'),#{obj.HW_NUM , jdbcType=DOUBLE },
       #{obj.VOL_A ,jdbcType= DOUBLE },#{obj.VOL_B ,jdbcType= DOUBLE },#{obj.VOL_C ,jdbcType= DOUBLE } from dual
    </ foreach > 
</ insert >

启动,调用这个方法的依然报错。。。

org.springframework.jdbc.BadSqlGrammarException:
### Error updating database.  Cause: java.sql.SQLException: ORA-01790: 表达式必须具有与对应表达式相同的数据类型

然后调试,发现VOL_A虽然是数字样子,但是我放入到map的是字符串。。。相当的无语,改成Double类型的放入到map中,然后重新调用到这个方法的时候。
看着屏幕上刷的数据,一种幸福感,满满的。。。
然后看下效率,比原来单条插入快了6倍!!!
最后想了下mybatis操作的时候#是会根据列的类型来判断是否需要添加引号,$不会加,然后将jdbcType=DOUBLE的字段换成了$,结果运行的时候没有报错。。但是数据库中相应字段没有数据啊!!!!!

<think>我们被要求回答关于MyBatis批量更新操作的问题。根据用户提供的引用内容,我们可以总结出四种主要的批量更新实现方式: 1. 方式一:使用foreach标签生成多条update语句(需要数据库允许执行多条SQL) 2. 方式二:使用IN条件进行批量更新(一次更新多条记录,但只更新相同值) 3. 方式三:使用case when语句进行批量更新(可以更新不同字段的不同值,性能较好) 4. 方式四:使用MyBatis的批处理器(BatchExecutor)在代码中执行批量操作 下面我们将分别详细说明这四种方式。 ### 1. 使用foreach标签生成多条update语句 这种方式在MyBatis的映射文件中使用foreach循环生成多个update语句,用分号分隔。但需要注意的是,默认情况下MySQL不允许一次执行多条SQL语句,因此需要在数据库连接URL中添加`allowMultiQueries=true`参数。 **XML配置示例:** ```xml <update id="updateBatch" parameterType="java.util.List"> <foreach collection="list" item="item" index="index" open="" close="" separator=";"> update tableName <set> name=#{item.name}, name2=#{item.name2} </set> where id = #{item.id} </foreach> </update> ``` **注意:** - 需要数据库支持多条SQL语句执行(MySQL需要添加`&allowMultiQueries=true`)[^1]。 - 这种方式在数据量很大时可能会因为SQL语句过多而导致性能下降。 ### 2. 使用IN条件进行批量更新 这种方式适用于将一批记录的某个字段更新为相同的值。它通过WHERE条件中的IN子句来指定需要更新的记录。 **XML配置示例:** ```xml <update id="batchUpdate" parameterType="int"> update user set name = 'xiaohao' where id in <foreach item="id" collection="list" open="(" separator="," close=")"> #{id} </foreach> </update> ``` **注意:** - 这种方式只能将多个记录的某个字段更新为相同的值。 - 对于更新不同记录的不同字段值,这种方式不适用。 ### 3. 使用case when语句进行批量更新(推荐) 这种方式可以一次性更新多条记录的不同字段值,且性能较好。它通过构建一个带有多个`when`条件的`case`语句来实现。 **XML配置示例:** ```xml <update id="updateBatch" parameterType="Test"> update table_test a <trim prefix="set" suffixOverrides=","> <trim prefix="a.test_value=case" suffix="end,"> <foreach collection="list" item="i" index="index"> <if test="i.testValue != null"> when a.test_id = #{i.testId} then #{i.testValue} </if> </foreach> </trim> <trim prefix="a.remarks=case" suffix="end,"> <foreach collection="list" item="i" index="index"> <if test="i.remarks != null"> when a.test_id = #{i.testId} then #{i.remarks} </if> </foreach> </trim> </trim> where a.test_id in <foreach collection="list" item="t" open="(" close=")" separator=","> #{t.testId} </foreach> </update> ``` **注意:** - 这种方式可以同时更新多个字段,且每个记录的字段值可以不同。 - 在数据量大的情况下,性能比前两种方式更好[^3]。 ### 4. 使用MyBatis的批处理器(BatchExecutor) 这种方式在代码层面使用MyBatis的批处理器,通过`ExecutorType.BATCH`模式执行批量更新。 **Java代码示例:** ```java SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false); YourMapper mapper = sqlSession.getMapper(YourMapper.class); try { for (YourEntity entity : entities) { mapper.update(entity); // 调用单条更新语句 } sqlSession.commit(); } finally { sqlSession.close(); } ``` **注意:** - 这种方式不需要在XML中写复杂的SQL,而是通过多次调用单条更新语句,然后由MyBatis进行批处理。 - 批处理执行效率较高,但需要数据库驱动程序的支持[^4]。 ### 总结 | 方式 | 适用场景 | 优点 | 缺点 | |------|----------|------|------| | foreach多条SQL | 更新字段不同且数据库允许执行多条SQL | SQL简单直观 | 需要数据库支持多条SQL,性能可能不佳 | | IN条件更新 | 批量更新相同值 | 简单高效 | 只能更新相同值 | | case when | 批量更新不同值 | 性能较好 | SQL较复杂 | | 批处理器 | 代码控制批量操作 | 代码清晰,支持事务 | 需要手动管理SqlSession | 根据实际需求选择合适的方式: - 如果更新字段值相同,推荐使用**IN条件更新**(方式二)。 - 如果更新字段值不同且数据量较大,推荐使用**case when方式**(方式三)。 - 如果对事务控制有要求,或者希望代码更简洁,可以使用**批处理器**(方式四)[^4]。 --- §§相关问题§§ 1. MyBatis批量更新时如何避免锁表? 2. MyBatis的批处理器(BatchExecutor)和普通执行器有什么区别? 3. 在MyBatis中如何实现批量插入操作? </think> ### MyBatis 批量更新操作指南 MyBatis 实现批量更新主要有四种方式,根据性能、灵活性和数据库兼容性可选择不同方案: --- ### 方式一:多条 SQL 语句批量更新(需数据库支持) ```xml <update id="updateBatch" parameterType="java.util.List"> <foreach collection="list" item="item" separator=";"> UPDATE tableName SET name = #{item.name}, name2 = #{item.name2} WHERE id = #{item.id} </foreach> </update> ``` **特点**: - 需在 MySQL 连接 URL 添加 `&allowMultiQueries=true` 参数[^1] - 每条记录生成独立 UPDATE 语句 - **缺点**:数据库需支持多语句执行 --- ### 方式二:IN 条件批量更新相同值 ```xml <update id="batchUpdate"> UPDATE user SET name = 'xiaohao' WHERE id IN <foreach item="id" collection="list" open="(" separator="," close=")"> #{id} </foreach> </update> ``` **适用场景**:批量更新**相同字段为相同值**[^2] --- ### 方式三:CASE WHEN 动态更新(推荐高性能方案) ```xml <update id="updateBatch" parameterType="Test"> UPDATE table_test <trim prefix="SET" suffixOverrides=","> <trim prefix="test_value = CASE" suffix="END,"> <foreach collection="list" item="i"> <if test="i.testValue != null"> WHEN test_id = #{i.testId} THEN #{i.testValue} </if> </foreach> </trim> <trim prefix="remarks = CASE" suffix="END,"> <foreach collection="list" item="i"> <if test="i.remarks != null"> WHEN test_id = #{i.testId} THEN #{i.remarks} </if> </foreach> </trim> </trim> WHERE test_id IN <foreach collection="list" item="t" open="(" separator="," close=")"> #{t.testId} </foreach> </update> ``` **优势**: - 单条 SQL 完成批量更新 - 支持不同记录更新不同值 - 数据库兼容性好(无需特殊配置) - **性能最佳**,尤其大数据量场景 --- ### 方式四:批处理器(BatchExecutor) ```java // 获取批处理模式的SqlSession SqlSession sqlSession = sqlSessionFactory .openSession(ExecutorType.BATCH, false); YourMapper mapper = sqlSession.getMapper(YourMapper.class); try { for (YourEntity entity : entities) { mapper.update(entity); // 单条更新操作 } sqlSession.commit(); // 统一提交 } finally { sqlSession.close(); } ``` **特点**: - JDBC 层面批量执行 Helen - 需手动管理事务提交 - 数据库驱动需支持批处理[^4] --- ### 方案对比 | 方式 | 性能 | 灵活性 | 数据库兼容性 | 适用场景 | |--------------|--------|------------|--------------|------------------------| | 多语句更新 | 中 | 高 | 需特殊配置 | 小数据量,多字段更新 | | IN条件更新 | 高 | 低 | 通用 | 批量更新相同值 | | CASE WHEN | **最高** | **最高** | **通用** | 大数据量差异化更新 | | 批处理器 | 高 | 中 | 依赖驱动 | 代码控制批量操作 | > 💡 **推荐选择**: > - 大数据量且字段值不同 → **方式三(CASE WHEN)** > - 批量更新相同值 → **方式二(IN条件)** > - 需要事务控制 → **方式四(批处理器)** ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值