批量插入有两个问题,第一是执行效率,第二数据冲突,第三数据重跑更新操作。
一般对于这样的问题有以下操作方法。
第一是执行效率:mybatis支持两种高效插入。
1.mybtis的foreach标签,foreach元素的属性主要有 item,index,collection,open,separator,close。
通过迭代把对应元素的属性批量插入。
<insert id="batchInsert"> insert into day_time(daily_year,daily_month,daily_week,daily_date,use_num,types) values <foreach collection="list" item="item" separator=","> (#{item.dailyYear},#{item.dailyMonth},#{item.dailyWeek},#{item.dailyDate},#{item.useNum},#{item.types}) </foreach> </insert>
2.mybatis ExecutorType.BATCH
Mybatis内置的ExecutorType有3种 :SimpleExecutor、ReuseExecutor、BatchExecutor
默认的是SimpleExecutor查询一次关闭一次每次查询都会重新开启statement,
ReuseExecutor用的不多,他不会关闭statement,以sql语句作为key相关的statement作为value,可以重复利用以前的创建好的statement,前提是sql必须一致,
也就是mybatis做boundsql解析之前的原生sql,不包括组装的参数,sql相同组装参数相同则代表解析boundsql一致一个session情况下是会直接走一级缓存直接调出结果。
然而ReuseExecutor使用的也就是很多数据连接池库中常见的 PSCache 概念 。
BatchExecutor是正真的批量执行器,而batch模式重复使用已经预处理的语句,并且批量执行所有更新语句,显然batch性能将更优;
但batch模式也有自己的问题,比如在Insert操作时,在事务没有提交之前,是没有办法获取到自增的id,这在某型情形下是不符合业务要求的。
但是批量模式下可以手动提交和回滚也是会有不同好处。
//获取sqlsession //从spring注入原有的sqlSessionTemplate @Autowired private SqlSessionTemplate sqlSessionTemplate; // 新获取一个模式为BATCH,自动提交为false的session // 如果自动提交设置为true,将无法控制提交的条数,改为最后统一提交,可能导致内存溢出 SqlSession session = sqlSessionTemplate.getSqlSessionFactory().openSession(ExecutorType.BATCH, false); //通过新的session获取mapper fooMapper=session.getMapper(FooMapper .class); int size = 10000; try { for (int i = 0; i < size; i++) { Foo foo = new Foo(); foo.setName(String.valueOf(System.currentTimeMillis())); fooMapper.insert(foo); if (i % 1000 == 0 || i == size - 1) { //手动每1000个一提交,提交后无法回滚 session.commit(); //清理缓存,防止溢出 session.clearCache(); } } }catch( Exception e) { //没有提交的数据可以回滚 session.rollback(); }finally { session.close(); }
第二数据冲突,第三数据重跑更新操作
这两个一起讲,因为一般情况下,数据重跑也会存在数据冲突,一般就主键以及唯一索引冲突。
在批量更插入一般这样的情况下,对于冲突的数据一般来说是要做更新操作的(前提是有做过自己的validation)。
但是又不可以方便对数据进行先查询再决定进行插入操作还是更新操作,这样批量操作时执行效率太低,。
mybatis批量中支持ON DUPLICATE KEY UPDATE用法。
也就是允许insert语句插入的行与表与现有记录的惟一索引或主键中产生重复值,那么就会发生旧行的更新;
如果插入的行数据与现有表中记录的唯一索引或者主键不重复,则执行新纪录插入操作。
<insert id="batchInsert"> insert into day_time(daily_year,daily_month,daily_week,daily_date,use_num,types) values <foreach collection="list" item="item" separator=","> (#{item.dailyYear},#{item.dailyMonth},#{item.dailyWeek},#{item.dailyDate},#{item.useNum},#{item.types}) </foreach> <!-- 有则更新 --> ON DUPLICATE KEY UPDATE use_num = values(use_num) </insert>