报错
SqlServer 表中主键设置为自增,会报以下错误。
org.springframework.jdbc.UncategorizedSQLException: Error getting generated key or setting result to parameter object. Cause: com.microsoft.sqlserver.jdbc.SQLServerException: 必须执行该语句才能获得结果
报错原因
原因:mybatis-plus 批量执行时 SQL server 自增主键没有回填造成的。
从 MyBatis3.3.1 版本开始,MyBatis 开始支持批量新增回写主键值的功能,这个功能首先要求数据库主键值为自增类型,同时还要求该数据库提供的 JDBC 驱动可以支持返回批量插入的主键值(JDBC提供了接口,但并不是所有数据库都完美实现了该接口),因此到目前为止,可以完美支持该功能的仅有 MySQL 数据库。由于 SQL Server 数据库官方提供的 JDBC 只能返回最后一个插入数据的主键值,所以不能支持该功能。
分析
查看调用栈,来到 BatchExecutor 的 doFlushStatements 方法,执行了 jdbc3KeyGenerator.processBatch(ms, stmt, parameterObjects)
进入方法,在如图位置抛出异常
解决
实现工具类,给 MappedStatement 设置 NoKeyGenerator,问题就解决了
public class SqlUtil {
/**
* 500条数据 提交一次
* 必须是 BATCH_CNT_SQL_SIZE 整数倍
*/
private final static int BATCH_SIZE = 500;
/**
* 批量保存,解决 mybatis-plus 在批量插入时由于主键自增报错问题
* 如果主键不是自增,不要调用
* @param list 数据集合
* @param mClazz mapper
* @return 操作结果
*/
public static <E, M extends BaseMapper<E>> boolean saveBatch(Class<M> mClazz, List<E> list) {
SqlSessionFactory factory = SpringUtil.getBean(SqlSessionFactory.class);
SqlSession sqlSession = factory.openSession(ExecutorType.BATCH, false);
MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(mClazz.getName() + ".insert");
MetaObject metaObject = SystemMetaObject.forObject(ms);
Object keyGenerator = metaObject.getValue("keyGenerator");
metaObject.setValue("keyGenerator", NoKeyGenerator.INSTANCE);
M mapper = sqlSession.getMapper(mClazz);
try {
// 用于跟踪自上次刷新以来已处理的元素数
int processedCount = 0;
for (E e : list) {
mapper.insert(e);
processedCount++;
if (processedCount >= BATCH_SIZE) {
sqlSession.flushStatements();
processedCount = 0;
}
}
// 如果还有剩余的元素未刷新,则刷新
if (processedCount > 0) {
sqlSession.flushStatements();
}
sqlSession.commit();
return true;
} catch (Throwable t) {
sqlSession.rollback();
throw new RuntimeException(t.getMessage());
} finally {
metaObject.setValue("keyGenerator", keyGenerator);
SqlSessionUtils.closeSqlSession(sqlSession, factory);
}
}
}