带有自定义绑定的 JOOQ 批量插入

Posted

技术标签:

【中文标题】带有自定义绑定的 JOOQ 批量插入【英文标题】:JOOQ Batch Insert with Custom Binding 【发布时间】:2021-09-08 09:27:33 【问题描述】:

我正在尝试在批量插入的上下文中使用 JOOQ 流式传输 BLOB 参数。根据stream contents of 1GB file to sqlite table under single column,数据类型绑定是流式传输参数参数的方式。按照它的例子,我实现了 Binding#set:

    @Override
    public void set(BindingSetStatementContext<KitItem> ctx) throws SQLException 
        PreparedStatement statement = ctx.statement();
        Blob blob = statement.getConnection().createBlob();
        KitItem item = ctx.value(); // The object to write to the stream

        try (OutputStream output = blob.setBinaryStream(1)) 
            // Main serialization implementation
         catch (IOException ex) 
            throw rethrow(ex);
        
        statement.setBlob(ctx.index(), blob);
        ctx.autoFree(blob);
    

这适用于简单的插入查询。但是,当我使用批量插入时,JOOQ 使用绑定的 Converter#from。我已经实现了 Converter#from 来抛出 UnsupportedOperationException。

        DataType<KitItem> itemType = KITPVP_KITS_CONTENTS.ITEM.getDataType().asConvertedDataType(new KitItemBinding());
        Field<KitItem> itemColumn = DSL.field(KITPVP_KITS_CONTENTS.ITEM.getQualifiedName(), itemType);
        if (true)  // Flipping this flag causes the Converter to be used
            for (ItemInSlot itemInSlot : contents) 
                context.insertInto(KITPVP_KITS_CONTENTS)
                        .columns(KITPVP_KITS_CONTENTS.KIT_ID, KITPVP_KITS_CONTENTS.SLOT, itemColumn)
                        .values(kitId, (byte) itemInSlot.slot(), itemInSlot.item())
                        .execute();
            
         else 
            BatchBindStep batch = context.batch(context
                    .insertInto(KITPVP_KITS_CONTENTS)
                    .columns(KITPVP_KITS_CONTENTS.KIT_ID, KITPVP_KITS_CONTENTS.SLOT, itemColumn)
                    .values((Field<Integer>) null, null, null));
            for (ItemInSlot itemInSlot : contents) 
                batch.bind(
                        kitId, itemInSlot.slot(), itemInSlot.item());
            
            batch.execute();
        

我怀疑这是我在使用 JOOQ 时的错误,因为我希望在设置参数时调用 Converter#to,而不是 Converter#from。即:

@Override
public KitItem from(byte[] databaseObject) 
    // I DO NOT expect this method to be called when setting parameters
    throw new UnsupportedOperationException("Conversion should be streamed instead");


@Override
public byte[] to(KitItem userObject) 
    // I expect this method or Binding#set to be called when setting parameters
    throw new UnsupportedOperationException("Conversion should be streamed instead");

我不确定这是否可能与Jooq batch insert with custom field binding 有关,但我已经检查了这个问题(也没有答案)。这两种行为都可能是由 JOOQ 中的一些实现错误引起的。我对 JOOQ 的了解还不够多。

【问题讨论】:

【参考方案1】:

JOOQ 调用转换器来处理初始值——即提供给values(null, null, null) 的初始参数,而不是提供给bind(obj1, obj2, ...) 的绑定变量。因此,只需传递这些 null 值,它就会起作用:

@Override
public KitItem from(byte[] databaseObject) 
    if (databaseObject == null) 
      return null;
    
    throw new UnsupportedOperationException("Conversion should be streamed instead");

其次,不要尝试执行空批处理。在尝试使用 JOOQ 执行批处理语句之前,请检查您要插入的数据是否为空。如果尝试执行一个空批处理,初始值(通常为 null)将作为单个插入查询执行。

【讨论】:

以上是关于带有自定义绑定的 JOOQ 批量插入的主要内容,如果未能解决你的问题,请参考以下文章

Mybatis 插入一条或批量插入 返回带有自增长主键记录

如何使用 JOOQ 批量执行

批量插入数据自定义分页器

如何在 JPA 和自定义 UUID 标识符中实现批量插入

Django 批量插入数据自定义分页器多表关系的建立及Form组件(待更新。。。)

BoneCP 抛出“SQLException:连接已关闭!”批量插入 MySQL 时