MyBatis执行truncate table二次调用报Index 0 out of bounds异常
问题:MyBatis执行TRUNCATE后多次插入出现索引越界错误
配置与代码
XML映射配置
<update id="truncateTable"> truncate TABLE ${tableName} </update>
业务方法代码
public void flushNeoReceivePayment() throws IOException { dwsTableSqlMapper.truncateTable("dwd_table"); dwsTableSqlMapper.insertSomething1(); dwsTableSqlMapper.insertSomething1(); }
insertSomething1方法用于插入部分数据
问题现象
- 首次调用
flushNeoReceivePayment方法完全正常 - 第二次调用时抛出错误,日志如下:
Creating a new SqlSession SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5368e1af] was not registered for synchronization because synchronization is not active JDBC Connection [HikariProxyConnection@1571751346 wrapping com.mysql.cj.jdbc.ConnectionImpl@72168258] will not be managed by Spring ==> Preparing: truncate dwd_table ==> Parameters: Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5368e1af] 2024-09-10 13:32:27.685 ERROR --- [nio-3009-exec-2] c.z.z.c.exceptions.GlobalExceptionHandle : org.springframework.dao.TransientDataAccessResourceException: ### Error updating database. Cause: java.sql.SQLException: Index 0 out of bounds for length 0 ### The error may involve defaultParameterMap ### The error occurred while setting parameters ### SQL: truncate dwd_table ### Cause: java.sql.SQLException: Index 0 out of bounds for length 0 ; Index 0 out of bounds for length 0; nested exception is java.sql.SQLException: Index 0 out of bounds for length 0
- 特殊场景测试结果:
- 只调用一次
insertSomething1时,方法执行正常:public void flushNeoReceivePayment() throws IOException { dwsTableSqlMapper.truncateTable("dwd_table"); dwsTableSqlMapper.insertSomething1(); } // 执行正常 - 将
truncate替换为delete语句时,无论调用多少次插入方法都不会报错
- 只调用一次
解决方案
原因分析
核心问题是TRUNCATE的DDL特性+非事务场景下MyBatis连接复用导致的参数绑定异常:
- TRUNCATE是DDL语句,执行后会自动提交事务并重置连接状态;
- 非事务模式下,MyBatis可能复用TRUNCATE执行后的连接,但该连接的参数绑定缓冲区状态与后续多次插入操作的逻辑冲突,引发索引越界错误。
可行解决办法
给方法添加事务管理
在flushNeoReceivePayment方法上添加@Transactional注解,让Spring管控整个方法的事务生命周期,确保连接在同一事务内正常复用:@Transactional public void flushNeoReceivePayment() throws IOException { dwsTableSqlMapper.truncateTable("dwd_table"); dwsTableSqlMapper.insertSomething1(); dwsTableSqlMapper.insertSomething1(); }强制TRUNCATE执行后刷新缓存
在XML映射中为TRUNCATE语句添加flushCache="true"属性,强制MyBatis执行后刷新缓存并重新获取连接,避免状态异常的连接被复用:<update id="truncateTable" flushCache="true"> truncate TABLE ${tableName} </update>替换TRUNCATE为DELETE(业务允许时)
如测试所示,使用DELETE FROM dwd_table替代TRUNCATE可规避该问题,但注意DELETE是DML语句,不会重置自增主键,且大数据量下性能弱于TRUNCATE,需根据业务场景选择。
内容的提问来源于stack exchange,提问作者laolupaojiao
相关产品推荐
相关产品推荐

