【Mybatis】Mybatis批量插入,并返回主键id

本文讲述了作者在使用Mybatis3.5.16操作MySQL8.x数据库时,尝试通过批处理执行器(ExecutorType.BATCH)插入数据并获取自增长ID遇到的问题,经过官方文档查阅和源码分析,发现ExecutorType.BATCH不支持ID生成,最后给出了解决方案:选择SIMPLE或REUSEExecutorType。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

运行环境

Mybatis 版本:3.5.16 

数据库:mysql8.X

场景

使用的Mybatis工具来操作DB数据,插入数据后返回对象的自增长序列ID

执行代码

PO类

public class TestPO{
    private int id;
    private int a;
    private int b;
   //省略getset!!!
}

apper接口,这里我用的是注解

public interface TestMapper{

    @Insert("insert into game_demo (a,b) values(#{a},#{b})")
    @SelectKey(statement = "select last_insert_id()",keyProperty = "id",before = false,resultType = int.class)
    int insert(TestVO testPO);

    @Insert("<script>insert into game_demo (a,b) <foreach item=\"item\" index=\"index\" collection=\"testPOS\" open=\"values\" separator=\",\" close=\";\" nullable=\"true\"> (#{item.a},#{item.b}) </foreach> </script>")
    @Options(useGeneratedKeys = true,keyProperty = "id",useCache = false)
    int inserts(@Param("testPOS") List<TestPO> testPOS);
}
​
 try(InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml")){
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
            Configuration configuration = sqlSessionFactory.getConfiguration();
            TypeAliasRegistry typeAliasRegistry = configuration.getTypeAliasRegistry();
            typeAliasRegistry.registerAlias(TestPO.class);
            configuration.addMapper(TestMapper.class);
            try(SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH)){
                TestMapper mapper = sqlSession.getMapper(TestMapper.class);
                List<TestPO> saves = new ArrayList<>();
                saves.add(new TestPO(3,1));
                saves.add(new TestPO(4,2));
                mapper.inserts(saves);
                for (TestPO save : saves) {
                    System.out.println("id==>"+save.getId());
                }
                sqlSession.commit();
            }
        }catch (Exception e){
            e.printStackTrace();
        }

​
错误
org.apache.ibatis.exceptions.PersistenceException: 
### Error committing transaction.  Cause: org.apache.ibatis.executor.ExecutorException: Error getting generated key or setting result to parameter object. Cause: java.lang.UnsupportedOperationException
### Cause: org.apache.ibatis.executor.ExecutorException: Error getting generated key or setting result to parameter object. Cause: java.lang.UnsupportedOperationException

Cause: java.lang.UnsupportedOperationException 这个看的我很懵逼,通过错误查看源码,找到了CollectionWrapper 抛出的错误点 hasSetter函数

Caused by: java.lang.UnsupportedOperationException
	at org.apache.ibatis.reflection.wrapper.CollectionWrapper.hasSetter(CollectionWrapper.java:73)
	at org.apache.ibatis.reflection.MetaObject.hasSetter(MetaObject.java:106)
	at org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator$KeyAssigner.assign(Jdbc3KeyGenerator.java:259)
public class CollectionWrapper implements ObjectWrapper {
    //省略若干!!!

    public boolean hasSetter(String name) {
        throw new UnsupportedOperationException();
    }

    public boolean hasGetter(String name) {
        throw new UnsupportedOperationException();
    }
}

是不是很操蛋,这是什么原因造成的呢!!!

问题排查
1.翻阅官方文档
2.注解补全参数
@Options(useGeneratedKeys = true,keyProperty = "testPOS.id",useCache = false)

通过看源码,即使这里不加testPOS,底层处理上也会自动补全

3.大量测试,阅读源码

通过以上两种方式最终定位在SqlSession上,SqlSession获取时有多个参数可调,来改变内部执行逻辑,上面代码中恰好我使用的是:

openSession(ExecutorType.BATCH)

对于ExecutorType 官方文档是这样描述的,这个枚举类型定义了三个值:

  • ExecutorType.SIMPLE:该类型的执行器没有特别的行为。它为每个语句的执行创建一个新的预处理语句。
  • ExecutorType.REUSE:该类型的执行器会复用预处理语句。
  • ExecutorType.BATCH:该类型的执行器会批量执行所有更新语句,如果 SELECT 在多个更新中间执行,将在必要时将多条更新语句分隔开来,以方便理解。

而我使用的是ExecutorType.BATCH,这个批量执行器从设计上就不支持ID返回

问题解决

在OpenSession指定以下ExecutorType之一即可:

ExecutorType.SIMPLE
ExecutorType.REUSE

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值