MyBatis insert 插入数据返回 -2147482646

1,182 阅读2分钟

你知道MyBatis insert插入数据的返回值是多少吗?是受影响的行数?或是其他值。返回值为-2147482646你知道是什么情况嘛?接下来来看一个测试用例

InputStream is = Resources.getResourceAsStream("sqlMapConfig.xml");
factory = new SqlSessionFactoryBuilder().build(is);
sqlSession = factory.openSession(ExecutorType.BATCH, true);
 UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, Object> params = new HashMap<>();
// Mapper中的SQL是insert into user (username,age) select username,age from user where id between ${start} and ${end}
params.put("start", 1);
params.put("end", 10);
Integer count = mapper.insertBatchUser(params);
System.out.println("受影响的行数:"+count);

日志打印结果

image.png

查询的SQL为

insert into user (username,age) select username,age from user where id between 1 and 10

一般地插入数据后返回值应该是受影响的行数。比如我插入5条数据,如果插入成功应该返回5。但是mybatis却给我返回了-2147482646,真是离谱!

原因

原因竟是SqlSession的ExecutorType导致的!当我把sqlSession = factory.openSession(ExecutorType.BATCH, true); 中的ExecutorType换成BATCH换成SIMPLE或是REUSE后插入数据的返回值就正常了,返回值就是受影响的行数而不是-2147482646

为什么会这样的?那就需要了解下MyBatis中的三种ExecutorType了

  • SIMPLE 对应SimpleExecutor简单执行器,平时我们默认就是用的它
  • BATCH 对应BatchExecutor批量执行器,底层是JDBC的Connection的executeBatch方法
  • RESUE 对应ResueExecutor重用执行器,重用Statement对象

mybatis执行插入SQL的时候会调用Executor的update方法(增删改最终都属于更新数据,所以调用的是update方法)查询数据库,Executor是一个接口,默认使用的SimpleExecutor这个实现类。而前述代码在创建SqlSession时指定的Executor的类型为Batch,那么mybatis插入数据库的时候就会调用BatchExecutor的doUpdate方法。我们来追踪一下BatchExecutor的doUpdate方法的执行逻辑

image.png

看到没,只要是使用了BatchExecutor,无论怎么插入数据,最后的返回值是固定的,都是Integer.MIN_VALUE + 1002即-2147482646

补充:SqlSession.insert的调用链路

下面是DefaultSqlSession的insert方法

public int insert(String statement, Object parameter) {
    return update(statement, parameter);
}

它委托给DefaultSqlSession的update方法执行插入操作,下面是DefaultSqlSession的update方法

public int update(String statement, Object parameter) {
    MappedStatement ms = configuration.getMappedStatement(statement);
    return executor.update(ms, wrapCollection(parameter));
}

可以看到SqlSession把update的任务委托给了Executor的update方法。而Executor的update方法默认是由抽象类BaseExecutor实现的。我们来看下BaseExecutor的update方法

public int update(MappedStatement ms, Object parameter) throws SQLException {
  if (closed) {
    throw new ExecutorException("Executor was closed.");
  }
  clearLocalCache();
  return doUpdate(ms, parameter);
}

可以看到它最终调用的doUpdate方法,这个doUpdate就是由不同类型的Executor的子类实现的。而BatchExecutor实现该方法的逻辑就是返回值固定为 -2147482646

public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
  // 省略中间逻辑
  return Integer.MIN_VALUE + 1002;
}