【SpringBoot DB系列】Jooq批量写入采坑记录

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 前面介绍了jooq的三种批量插入方式,结果最近发现这里面居然还有一个深坑,我以为的批量插入居然不是一次插入多条数据,而是一条一条的插入...,这就有点尬了

image.png

【SpringBoot DB系列】Jooq批量写入采坑记录


前面介绍了jooq的三种批量插入方式,结果最近发现这里面居然还有一个深坑,我以为的批量插入居然不是一次插入多条数据,而是一条一条的插入...,这就有点尬了


1. 三种插入姿势



关于项目创建以及jooq的相关使用姿势,推荐查看之前的博文: 【DB系列】Jooq之新增记录使用姿势


下面是我们采用的三种批量插入方式


/**
 * 通过Record执行批量添加
 *
 * 通过源码查看,这种插入方式实际上是单条单条的写入数据,和下面的一次插入多条有本质区别
 *
 * @param list
 * @return
 */
public boolean batchSave(List<PoetBO> list) {
    List<PoetPO> poList = list.stream().map(this::bo2po).collect(Collectors.toList());
    int[] ans = dsl.batchInsert(poList).execute();
    System.out.println(JSON.toJSONString(ans));
    return true;
}
/**
 * 类sql写法,批量添加
 *
 * @param list
 * @return
 */
public boolean batchSave2(List<PoetBO> list) {
    InsertValuesStep2<PoetPO, Integer, String> step = dsl.insertInto(table).columns(table.ID, table.NAME);
    for (PoetBO bo : list) {
        step.values(bo.getId(), bo.getName());
    }
    return step.execute() > 0;
}
/**
 * 不基于自动生成的代码,来批量添加数据
 *
 * @param list
 * @return
 */
public boolean batchSave3(List<PoetBO> list) {
    InsertQuery insertQuery = dsl.insertQuery(DSL.table("poet"));
    for (PoetBO bo : list) {
        insertQuery.addValue(DSL.field("id", Integer.class), bo.getId());
        insertQuery.addValue(DSL.field("name", String.class), bo.getName());
        insertQuery.newRecord();
    }
    return insertQuery.execute() > 0;
}
复制代码


请注意上面的三种批量插入方式,基本上对应的就是jooq的三种常见的用法


  • 直接借助自动生成的Record类来操作
  • 类sql的拼接写法,基本上我们平时的sql怎么写,这里就怎么用
  • InsertQuery:借助jooq提供的各种Query类来执行目标操作


2. 日志验证



上面三种写法中,第一种批量插入方式,并不是我们传统理解的一次插入多条记录,相反它是一条一条的插入的,我们可以通过开启jooq的日志来查看一些执行的sql情况

配置文件 application.properties,添加下面的配置


debug=false
trace=false
logging.level.org.jooq=DEBUG
复制代码


如果有自己的logback.xml配置文件,可以调整一下日志级别,将jooq的debug日志放出来


一个简单的测试case

public void test() {
  this.batchSave(Arrays.asList(new PoetBO(14, "yh"), new PoetBO(15, "yhh")));
  this.batchSave2(Arrays.asList(new PoetBO(16, "yihui"), new PoetBO(17, "yihuihui")));
  this.batchSave3(Arrays.asList(new PoetBO(18, "YiHui"), new PoetBO(19, "YiHuiBlog")));
}
复制代码

image.png


从上面的sql来看,后面两个确实是一次插入多条,但是第一个,也没有将具体执行的sql打印出来,所有不看源码的话,也没有办法实锤是一条一条插入的


为了验证这个问题,一个简单的解决办法就是批量插入两条数据,第一条正常,第二条异常,如果第一条插入成功,第二条失败那就大概率是单个插入的了


// 表结构中,name的字段最大为20,下面插入的第二条数据长度超限
try {
    this.batchSave(Arrays.asList(new PoetBO(14, "yh"), new PoetBO(15, "1234567890098765432112345")));
} catch (Exception e) {
    e.printStackTrace();
}
try {
    this.batchSave2(Arrays.asList(new PoetBO(16, "yihui"), new PoetBO(17, "1234567890098765432112345")));
} catch (Exception e) {
    e.printStackTrace();
}
this.batchSave3(Arrays.asList(new PoetBO(18, "YiHui"), new PoetBO(19, "YiHuiBlog")));
复制代码

第一种批量插入失败

image.png

第二种插入失败

网络异常,图片无法展示
|

插入后结果

image.png


请注意上面的报错,以及最终插入的结果,第一种插入方式一个插入成功一个失败;第二种批量插入方式,两条都插入失败;


通常情况下,一次插入多条数据时,一个插入失败,会导致整个插入都失败,如下


image.png


3. 源码分析


上面是从日志以及结果表现来推测实际的执行情况,接下来就需要从源码角度来看一下,是否真的是单个的执行了


省略掉具体的定位过程,直接找到org.jooq.impl.BatchCRUD#execute,对应的代码

@Override
public final int[] execute() throws DataAccessException {
    // [#1180] Run batch queries with BatchMultiple, if no bind variables
    // should be used...
    if (executeStaticStatements(configuration.settings())) {
        return executeStatic();
    }
    else {
        return executePrepared();
    }
}
复制代码


上面有两种插入方式,对于插入的核心逻辑一样

image.png

遍历集合,获取单个 record,执行 CURD

image.png



相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
Java
springboot中redisTemplate指定使用DB
springboot中redisTemplate指定使用DB
917 0
|
XML SQL Java
【SpringBoot DB 系列】Mybatis 基于 AbstractRoutingDataSource 与 AOP 实现多数据源切换
前面一篇博文介绍了 Mybatis 多数据源的配置,简单来讲就是一个数据源一个配置指定,不同数据源的 Mapper 分开指定;本文将介绍另外一种方式,借助AbstractRoutingDataSource来实现动态切换数据源,并通过自定义注解方式 + AOP 来实现数据源的指定
320 1
【SpringBoot DB 系列】Mybatis 基于 AbstractRoutingDataSource 与 AOP 实现多数据源切换
|
NoSQL Java Redis
(Redis使用系列) Springboot 实现Redis 同数据源动态切换db 八
(Redis使用系列) Springboot 实现Redis 同数据源动态切换db 八
1115 0
(Redis使用系列) Springboot 实现Redis 同数据源动态切换db 八
|
XML Java 关系型数据库
【SpringBoot DB系列】Mybatis多数据源配置与使用
上一篇博文介绍 JdbcTemplate 配置多数据源的使用姿势,在我们实际的项目开发中,使用 mybatis 来操作数据库的可能还是非常多的,本文简单的介绍一下 mybatis 中,多数据源的使用姿势
577 0
【SpringBoot DB系列】Mybatis多数据源配置与使用
|
XML Java 关系型数据库
【SpringBoot DB 系列】Mybatis-Plus 多数据源配置
MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,既然做增强,那多数据源这种硬性场景,肯定是有非常简单的解决方案的 本文将实例演示 Mybatis-Plus 多数据源的配置
714 0
【SpringBoot DB 系列】Mybatis-Plus 多数据源配置
|
存储 NoSQL 算法
【SpringBoot DB 系列】Redis 高级特性之 HyperLoglog
hyperloglog 算法,利用非常少的空间,实现比较大的数据量级统计;比如我们前面在介绍 bitmap 的过程中,说到了日活的统计,当数据量达到百万时,最佳的存储方式是 hyperloglog,本文将介绍一下 hyperloglog 的基本原理,以及 redis 中的使用姿势
301 0
|
1月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,包括版本兼容性、安全性、性能调优等方面。
137 1
|
16天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。首先,创建并配置 Spring Boot 项目,实现后端 API;然后,使用 Ant Design Pro Vue 创建前端项目,配置动态路由和菜单。通过具体案例,展示了如何快速搭建高效、易维护的项目框架。
94 62
|
13天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,帮助开发者提高开发效率和应用的可维护性。
32 2
|
16天前
|
JavaScript Java 项目管理
Java毕设学习 基于SpringBoot + Vue 的医院管理系统 持续给大家寻找Java毕设学习项目(附源码)
基于SpringBoot + Vue的医院管理系统,涵盖医院、患者、挂号、药物、检查、病床、排班管理和数据分析等功能。开发工具为IDEA和HBuilder X,环境需配置jdk8、Node.js14、MySQL8。文末提供源码下载链接。