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

运行环境

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

  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: MyBatis可以使用批量插入来实现批量新增数据,而在Oracle数据库中,可以通过使用序列来自动生成主键,然后将主键值插入到相关的表中。 在MyBatis中,通常使用`insertBatch`语句来实现批量新增,可以通过定义一个`List`来作为参数,每个元素代表一个要新增的对象,然后使用#{属性名}来引用相应的属性值。在执行批量新增时,也可以设置`batch`元素的`size`属性来指定批处理的大小,以优化性能。 此外,为了在新增数据时返回主键值,可以使用`selectKey`语句来获取生成的主键值。在Oracle中,可以通过设置`selectKey`语句的`order`属性为`BEFORE`来让它在新增数据之前获取主键值,然后将主键值设置到相应的对象中。 例如,以下是一个使用MyBatis和Oracle批量新增并返回主键的示例: ``` <insert id="insertBatch" parameterType="java.util.List"> <selectKey keyProperty="id" order="BEFORE" resultType="java.lang.Long"> SELECT SEQ_ID.NEXTVAL FROM DUAL </selectKey> insert into USER (ID, NAME, AGE) values (#{id}, #{name}, #{age}) </insert> ``` 在上述示例中,`insertBatch`语句插入一个`List`中包含的多个`User`对象,通过`selectKey`语句获取自动生成的主键值并设置到相应的对象中。 总之,通过结合MyBatis和Oracle的相关特性,可以实现批量新增并返回主键的功能,以提高数据插入的性能和效率。 ### 回答2: MyBatis是一种JAVA持久层框架,使用MyBatis可以让程序员将数据库查询语句和JAVA代码相分离,很大程度上减少了编程量和维护成本。Oracle是一种常用的关系型数据库管理系统,是企业应用的首选。 在MyBatis Oracle批量新增数据时,我们需要使用Oracle的SEQUENCE(序列)来生成主键。SEQUENCE是Oracle的一种内部对象,能够生成一个唯一的数字序列,可以用于自动分配主键。我们需要在MyBatis XML配置文件中定义一个带有“selectKey”标签的insert语句,这个标签会让MyBatis在执行insert语句之前先查询SEQUENCE,然后将取得的结果作为主键插入到数据库中。 具体操作流程如下: 1. 创建一个Sequence。 CREATE SEQUENCE seq_test MINVALUE 1 MAXVALUE 999999999999999999999999999 START WITH 1 INCREMENT BY 1 CACHE 20; 2.在MyBatis XML配置文件中定义一个insert语句,并在该语句下面用“selectKey”标签来声明取得主键的方式。其中,“order”属性表示主键生成的顺序,可以选择“before”或“after”;“keyProperty”属性表示主键生成后存储在哪一个对象属性中,一般情况下是实体类的主键属性;“resultType”属性表示主键类型。 <insert id="batchInsert" parameterType="java.util.List"> <foreach collection="list" item="item" separator=";"> insert into table_test (id, name) values(seq_test.nextval, #{item.name}) </foreach> <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Long"> SELECT seq_test.currval FROM DUAL </selectKey> </insert> 3.在Java代码中调用该insert语句,并将数据存储在List中。 List<TestEntity> testList = new ArrayList<>(); for (int i = 0; i < 10; i++) { TestEntity test = new TestEntity(); test.setName("name_" + i); testList.add(test); } testMapper.batchInsert(testList); 4.执行以上代码后,MyBatis会根据insert语句将数据批量插入数据库中,并通过SEQUENCE生成主键。在insert语句执行完之后,MyBatis会自动查询主键并将其存储在实体类的主键属性中。我们可以通过遍历List来获取新插入数据的主键值: for (TestEntity test : testList) { System.out.println(test.getId()); } 总之,MyBatis和Oracle的配合使用可以实现批量新增并返回主键的操作。使用Oracle的SEQUENCE可以保证主键值的唯一性,MyBatis的XML配置文件中使用selectKey标签可以获取新插入数据的主键值。 ### 回答3: 在 MyBatis 中,可以通过使用批量插入操作来实现在 Oracle 数据库中批量新增并返回主键值。 实现步骤如下: 1. 首先需要在 mapper.xml 文件中定义一个 insertList 节点,用于批量插入数据: ``` <insert id="insertList" parameterType="java.util.List"> <selectKey keyProperty="id" order="BEFORE" resultType="java.lang.Long"> SELECT SEQ.nextval as id FROM dual </selectKey> insert into table_name (id, col1, col2, col3) values (#{id}, #{col1}, #{col2}, #{col3}) </insert> ``` 其中,keyProperty 属性指定主键的属性名称,order 属性指定主键生成的顺序,resultType 属性指定主键的数据类型(这里使用了 Long 类型)。 2. 在 Java 代码中,通过 sqlSession 的 insert 方法来执行批量新增操作: ``` List<Data> dataList = ...; // 构造待插入的数据列表 for (Data data : dataList) { sqlSession.insert("insertList", data); } sqlSession.commit(); // 提交事务 ``` 在执行插入操作时,MyBatis 会自动调用 selectKey 节点中指定的 SQL 语句来生成主键值,并将其赋值给对应的属性。最终,主键值会被映射回 Java 对象中,以便后续使用。 需要注意的是,在 Oracle 数据库中,使用批量插入操作时,需要将 autoCommit 属性设置为 false,以确保所有插入操作都在同一事务中执行。 总结:通过 MyBatis批量插入操作,我们可以很方便地实现在 Oracle 数据库中批量新增并返回主键值的功能。在实现过程中,需要注意正确配置 mapper.xml 文件,并将 autoCommit 属性设置为 false,以确保操作在同一事务中执行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值