JOOQ用法和实例

Posted BLKNjy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JOOQ用法和实例相关的知识,希望对你有一定的参考价值。

这里写目录标题

JOOQ简介

jOOQ,是一个ORM框架,利用其生成的Java代码和流畅的API,可以快速构建有类型约束的安全的SQL语句 。

优点:

  1. jOOQ的核心优势是可以将数据库表结构映射为Java类,包含表的基本描述和所有表字段。通过jOOQ提供的API,配合生成的Java代码,可以很方便的进行数据库操作

  2. 生成的Java代码字段类型是根据数据库映射成的Java类型,在进行设置和查询操作时,因为是Java代码,都会有强类型校验,所以对于数据的输入,是天然安全的,极大的减少了SQL注入的风险

  3. jOOQ的代码生成策略是根据配置全量生成,任何对于数据库的改动,如果会影响到业务代码,在编译期间就会被发现,可以及时进行修复

CRUD

所有的操作jooq都提供两种方式, 第一种是使用 DSLContext API 以类SQL的语法进行调用,第二种是利用 Record API 进行调用 。这里面只记录第一种,了解第二种请点击这里

了解:

  • dslContext 代表DSLContext实例
  • S1_USER 由jOOQ插件生成的表描述常量
  • S1_USER.* 由jOOQ插件生成的表内字段常量

了解三个接口:

  • org.jooq.Result 结果集接口,此接口实现了List接口,可以当做一个集合来操作,是一个数据库查询结果集的包装类,除了集合的相关方法,该接口还提供了一些结果集转换,格式化,提取字段等方法。通常我们查询出来的结果都是此接口的实现类,掌握好此接口是jOOQ的基础接口,基本所有的SQL查询操作,都会碰到这个接口
  • org.jooq.Record 此接口再使用关系型数据库时,主要用于定义数据库表记录,储存的内容是一条表记录的字段和值,每个值会储存对应字段的类型,可以通过通用的 getValue(Field field) 方法,取到对应字段的值,也可以将这个接口看做是一条记录的字段/值映射
  • org.jooq.DSLContext jOOQ的核心接口之一,可以理解为一个SQL执行器,通过静态方法 DSL.using,可以获取一个 DSLContext 实例,此实例抽象了所有对于SQL的操作API,可以通过其提供的API方便的进行SQL操作

INSERT

// 类SQL语法 insertInto 方法第一个参数通常是表常量
dslContext.insertInto(S1_USER, S1_USER.USERNAME, S1_USER.ADDRESS, S1_USER.EMAIL)
        .values("username1", "demo-address1", "diamondfsd@gmail.com")
        .values("username2", "demo-address2", "diamondfsd@gmail.com")
        .execute();
 //批量插入
 List<S1UserRecord> recordList = IntStream.range(0, 10).mapToObj(i -> 
    S1UserRecord s1UserRecord = new S1UserRecord();
    s1UserRecord.setUsername("usernameBatchInsert" + i);
    s1UserRecord.setEmail("diamondfsd@gmail.com");
    return s1UserRecord;
).collect(Collectors.toList());
dslContext.batchInsert(recordList).execute();
//插入后获取主键
//通过此方法插入数据,可以通过 returning API读取想要返回的数据,此语法支持返回多个值,通过fetchOne()方法可以取到一个Record对象
Integer userId = dslContext.insertInto(S1_USER,
    S1_USER.USERNAME, S1_USER.ADDRESS, S1_USER.EMAIL)
    .values("username1", "demo-address1", "diamondfsd@gmail.com")
    .returning(S1_USER.ID)
    .fetchOne().getId();
//插入时主键重复的处理办法
// 第一种 :这里执行完,返回affecteRow影响行数为0,即不生效
// 生成的SQL: insert ignore into `learn-jooq`.`s1_user` (`id`, `username`) values (1, 'username-1')
int affecteRow = dslContext.insertInto(S1_USER,
    S1_USER.ID, S1_USER.USERNAME)
    .values(1, "username-1")
    .onDuplicateKeyIgnore()
    .execute();
// 第二种:更新主键所在列
//生成SQL: insert into `learn-jooq`.`s1_user` (`id`, `username`, `address`) values (1, 'duplicateKey-update', 'hello world') on duplicate key update `learn-jooq`.`s1_user`.`username` = 'duplicateKey-update', `learn-jooq`.`s1_user`.`address` = 'update'
dslContext.insertInto(S1_USER)
    .set(S1_USER.ID, 1)
    .set(S1_USER.USERNAME, "duplicateKey-insert")
    .set(S1_USER.ADDRESS, "hello world")
    .onDuplicateKeyUpdate()
    .set(S1_USER.USERNAME, "duplicateKey-update")
    .set(S1_USER.ADDRESS, "update")
    .execute();

update

dslContext.update(S1_USER)
    .set(S1_USER.USERNAME, "apiUsername-1")
    .set(S1_USER.ADDRESS, "update-address")
    .where(S1_USER.ID.eq(1))
    .execute()
    
//批量更新
  S1UserRecord record1 = new S1UserRecord();
record1.setId(1);
record1.setUsername("batchUsername-1");
S1UserRecord record2 = new S1UserRecord();
record2.setId(2);
record2.setUsername("batchUsername-2");

List<S1UserRecord> userRecordList = new ArrayList<>();
userRecordList.add(record1);
userRecordList.add(record2);
dslContext.batchUpdate(userRecordList).execute();

select

基本查询方法,默认查询指定表的所有字段,返回一个结果集的包装,通过Result.into方法,可以将结果集转换为任意指定类型集合,当然也可以通过 Record.getValue 方法取得任意字段值,值类型依赖于字段类型

// select `learn-jooq`.`s1_user`.`id`, `learn-jooq`.`s1_user`.`username`, `learn-jooq`.`s1_user`.`email`, `learn-jooq`.`s1_user`.`address`, `learn-jooq`.`s1_user`.`create_time`, `learn-jooq`.`s1_user`.`update_time` from `learn-jooq`.`s1_user`
Result<Record> fetchResult = dslContext.select().from(S1_USER).fetch();
List<S1UserRecord> result = fetch.into(S1UserRecord.class);

// select `learn-jooq`.`s1_user`.`id`, `learn-jooq`.`s1_user`.`username`, `learn-jooq`.`s1_user`.`email`, `learn-jooq`.`s1_user`.`address`, `learn-jooq`.`s1_user`.`create_time`, `learn-jooq`.`s1_user`.`update_time` from `learn-jooq`.`s1_user` where `learn-jooq`.`s1_user`.`id` in (1, 2)
Result<Record> fetchAll = dslContext.select().from(S1_USER)
                .where(S1_USER.ID.in(1, 2)).fetch();
fetchAll.forEach(record -> 
    Integer id = record.getValue(S1_USER.ID);
    String username = record.getValue(S1_USER.USERNAME);
    String address = record.getValue(S1_USER.ADDRESS);
    Timestamp createTime = record.getValue(S1_USER.CREATE_TIME);
    Timestamp updateTime = record.getValue(S1_USER.UPDATE_TIME);
);

jooq也支持关联查询

//UserMessagePojo为新建的Pojo类,用于存储查询结果,可以忽略具体内容
Result<Record3<String, String, String>> record3Result =
        dslContext.select(S1_USER.USERNAME,
        S2_USER_MESSAGE.MESSAGE_TITLE,
        S2_USER_MESSAGE.MESSAGE_CONTENT)
        .from(S2_USER_MESSAGE)
        .leftJoin(S1_USER).on(S1_USER.ID.eq(S2_USER_MESSAGE.USER_ID))
        .fetch();
List<UserMessagePojo> userMessagePojoList = record3Result.into(UserMessagePojo.class)

Condition动态查询

public void query(String name,String age) 
		Condition condition=DSL.trueCondition();//真实条件
		if(name!=null) 
			condition=condition.and(Tables.STUDENT.NAME.eq(name));
		
		if(age!=null) 
			condition=condition.and(Tables.STUDENT.AGE.eq(Integer.parseInt(age)));
		
		List<Student> list=context.select().from(Tables.STUDENT).where(condition).fetch().into(Student.class);
	

Delete

dslContext.delete(S1_USER).where(S1_USER.USERNAME.eq("demo1")).execute();

//批量删除
S1UserRecord record1 = new S1UserRecord();
record1.setId(1);
S1UserRecord record2 = new S1UserRecord();
record2.setId(2);
dslContext.batchDelete(record1, record2).execute();
// 
List<S1UserRecord> recordList = new ArrayList<>();
recordList.add(record1);
recordList.add(record2);
dslContext.batchDelete(recordList).execute();

结果处理

查询操作通常以fetch API 作为结束API,例如常用的有,所有的读取类方法都差不多,掌握一个就能很快的举一反三

  • 读取多条
    • fetch 读取集合
    • fetchSet 读取并返回一个Set集合,常用于去重
    • fetchArray 读取并返回一个数组
  • 读取单条
    • fetchOne 读取单条记录,如果记录超过一条会报错
    • fetchAny 读取单条记录,如果有多条,会取第一条数据
    • fetchSingle 读取单条记录,如果记录为空或者记录超过一条会报错
  • 读取并返回Map
    • fetchMap 读取并返回一个Map
    • fetchGroups 读取并返回一个分组Map

fetch

  • fetch()
    无参调用此方法,返回的是一个Result结果集对象

    Result<Record> records = dslContext.select().from(S1_USER).fetch();
    
  • fetch(RecordMapper mapper)
    RecordMapper接口的提供map方法,用于来返回数据。map 方法传入一个 Record 对象。可以使用lambda表达式将 Record 对象转换成一个指定类型的POJO

    List<S1UserPojo> userPojoList = dslContext.select()
                .from(S1_USER)
                .where(S1_USER.ID.eq(1))
                .fetch(r -> r.into(S1UserPojo.class));
    

    多表查询,字段相同时,直接用into方法将结果集转换为POJO时,相同字段名称的方法会以最后一个字段值为准。这时候,我们可以现将结果集通过 into(Table table) 方法将结果集转换为指定表的Record对象,然后再into进指定的POJO类中

    // 多表关联查询,查询s2_user_message.id = 2的数据,直接into的结果getId()却是1
    // 这是因为同时关联查询了s1_user表,该表的id字段值为1
    List<S2UserMessage> userMessage = dslContext.select().from(S2_USER_MESSAGE)
            .leftJoin(S1_USER).on(S1_USER.ID.eq(S2_USER_MESSAGE.USER_ID))
            .where(S2_USER_MESSAGE.ID.eq(2))
            .fetch(r -> r.into(S2UserMessage.class));
    // userMessage.getId() == 1
    
    // 将结果集into进指定的表描述中,然后在into至指定的POJO类
    List<S2UserMessage> userMessage2 = dslContext.select().from(S2_USER_MESSAGE)
            .leftJoin(S1_USER).on(S1_USER.ID.eq(S2_USER_MESSAGE.USER_ID))
            .where(S2_USER_MESSAGE.ID.eq(2))
            .fetch(r -> 
                S2UserMessage fetchUserMessage = r.into(S2_USER_MESSAGE).into(S2UserMessage.class);
                fetchUserMessage.setUsername(r.get(S1_USER.USERNAME));
                return fetchUserMessage;
            );
    // userMessage.getId() == 2
    
  • fetch(Field field)
    Field是一个接口,代码生成器生成的表字段常量例如 S1_USER.ID, 都实现了 Field 接口,这个重载可以直接取出指定表字段,会自动根据传入的字段推测其类型

    List<Integer> id = dslContext.select().from(S1_USER).where(S1_USER.ID.eq(1))
            .fetch(S1_USER.ID);
    
  • fetch(String fieldName, Class type)
    可以直接通过字段名称字符串获取指定字段值,可以通过第二个参数指定返回值,如果不指定,返回Object

    List<Integer> idList = dslContext.select().from(S1_USER).where(S1_USER.ID.eq(1))
            .fetch("id", Integer.class);
    
  • fetch(int fieldIndex, Class type)
    可以通过查询字段下标顺序进行查询指定字段,可以通过第二个参数指定返回值,如果不指定,返回Object

    List<Integer> idList = dslContext.select(S1_USER.ID, S1_USER.USERNAME)
            .from(S1_USER).where(S1_USER.ID.eq(1)).fetch(0, Integer.class);
    

fetch

此方法可以将结果集处理为一个Map格式,此方法有很多重载,这里介绍几个常用的,注意,此方法作为key的字段必须确定是在当前结果集中是唯一的,如果出现重复key,此方法会抛出异常

  • fetchMap(Field field, Class type)
    以表字段值为key,返回一个 K:V 的Map对象

    Map<Integer, S1UserPojo> idUserPojoMap = dslContext.select().from(S1_USER)
                    .fetchMap(S1_USER.ID, S1UserPojo.class);
    
  • fetchMap(Feild field, Field field)
    以表字段值为key,返回一个 K:V 的Map对象

    Map<Integer, String> idUserNameMap = dslContext.select().from(S1_USER)
                    .fetchMap(S1_USER.ID, S1_USER.USERNAME);
    

fetchgroup

此方法可以将结果集处理为一个Map格式,和fetchMap类似,只不过这里的值为一个指定类型的集合,通常在处理一对多数据时会用到

  • fetchGroups(Field field, Class type)
    以表字段值为Key,返回一个K:List 的Map对象

    Map<Integer, List<S2UserMessage>> userIdUserMessageMap = dslContext.select().from(S2_USER_MESSAGE)
                    .fetchGroups(S2_USER_MESSAGE.USER_ID, S2UserMessage.class);
    
  • fetchGroups(Field keyField, Field valueField)

  • 以表字段值为Key,返回一个K:List的Map对象

    Map<Integer, List<Integer>> userIdUserMessageIdMap = dslContext.select().from(S2_USER_MESSAGE)
                    .fetchGroups(S2_USER_MESSAGE.USER_ID, S2_USER_MESSAGE.ID);
    

参考JOOQ中文文档教程

更多用法参考

jooq multiset jsonb 列失败,无法构造 `org.jooq.JSONB` 的实例

【中文标题】jooq multiset jsonb 列失败,无法构造 `org.jooq.JSONB` 的实例【英文标题】:jooq multiset jsonb column fails with Cannot construct instance of `org.jooq.JSONB` 【发布时间】:2021-11-30 19:51:14 【问题描述】:

我第一次体验了 jooqs 新的多集功能。 有一个产品表,每个产品可以分配可变数量的存储。每个存储可能有一个 storage_coordinate_instance,而一个 storage_coordinate_instance 有一个列层次结构,它保存存储位置的解析递归表示。 (更新非常少,存储层次结构避免了每次需要存储位置时解析递归查询)。

一切都很好,直到我尝试将层次结构列添加到多重集。

这是查询:

    List<ProductFilterItem> items =
        dslContext
            .select(
                PRODUCT.ID,
                PRODUCT.NAME,
                PRODUCT.ARTICLE_NUMBER,
                PRODUCT.PHYSICAL,
                multiset(
                    select(
                        PRODUCT_STORAGE.ID,
                        PRODUCT_STORAGE.PRODUCT_ID,
                        PRODUCT_STORAGE.STOCK,
                        PRODUCT_STORAGE.COORDINATE_INSTANCE_ID,
                        STORAGE_COORDINATE_INSTANCE.HIERARCHY
                    )
                        .from(PRODUCT_STORAGE)
                        .leftOuterJoin(STORAGE_COORDINATE_INSTANCE).on(STORAGE_COORDINATE_INSTANCE.ID.eq(PRODUCT_STORAGE.COORDINATE_INSTANCE_ID))
                        .where(PRODUCT_STORAGE.PRODUCT_ID.eq(PRODUCT.ID))
                ).as("storage").convertFrom(r -> r.into(ProductStorageItem.class))
            )
            .from(PRODUCT)
            .where(queryCondition)
            .fetchInto(ProductItem.class)

这就是存储项映射到的类:

  private static class ProductStorageItem 

    private final UUID id;
    private final UUID productId;
    private final Double stock;
    private final UUID coordinateInstanceId;
    private final JSONB hierarchy;

    public ProductStorageItem(UUID id, UUID productId, Double stock, UUID coordinateInstanceId, JSONB hierarchy) 
      this.id = id;
      this.productId = productId;
      this.stock = stock;
      this.coordinateInstanceId = coordinateInstanceId;
      this.hierarchy = hierarchy;
    

如果当前过滤未返回分配了 storage_coordinate_instance 的产品,则查询不会出错。

否则会产生

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `org.jooq.JSONB` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: (String)""606f4346-292e-4479-a2a1-39bb49e44873":"2","8d86347f-fc31-4f88-92c4-6e4f5fd12626":"2","d2bbbce0-1487-4f87-b458-07c4e8a54065":"2""; line: 1, column: 2]
    at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63) ~[jackson-databind-2.12.3.jar:2.12.3]
    at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1588) ~[jackson-databind-2.12.3.jar:2.12.3]
    at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1213) ~[jackson-databind-2.12.3.jar:2.12.3]
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1415) ~[jackson-databind-2.12.3.jar:2.12.3]
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:362) ~[jackson-databind-2.12.3.jar:2.12.3]
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:195) ~[jackson-databind-2.12.3.jar:2.12.3]
    at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:322) ~[jackson-databind-2.12.3.jar:2.12.3]
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4593) ~[jackson-databind-2.12.3.jar:2.12.3]
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3548) ~[jackson-databind-2.12.3.jar:2.12.3]
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3516) ~[jackson-databind-2.12.3.jar:2.12.3]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
    at org.jooq.impl.Convert$ConvertAll.from(Convert.java:1139) ~[jooq-3.15.3.jar:na]
    at org.jooq.impl.Convert.convert0(Convert.java:426) ~[jooq-3.15.3.jar:na]
    at org.jooq.impl.Convert.convert(Convert.java:501) ~[jooq-3.15.3.jar:na]
    at org.jooq.impl.AbstractDataType.convert(AbstractDataType.java:538) ~[jooq-3.15.3.jar:na]
    at org.jooq.impl.DefaultDataType.convert(DefaultDataType.java:97) ~[jooq-3.15.3.jar:na]
    at org.jooq.impl.ConvertedDataType.convert(ConvertedDataType.java:224) ~[jooq-3.15.3.jar:na]
    at org.jooq.impl.Tools.setValue(Tools.java:3068) ~[jooq-3.15.3.jar:na]
    at org.jooq.impl.DefaultRecordUnmapper$IterableUnmapper.unmap(DefaultRecordUnmapper.java:189) ~[jooq-3.15.3.jar:na]
    at org.jooq.impl.DefaultRecordUnmapper.unmap(DefaultRecordUnmapper.java:102) ~[jooq-3.15.3.jar:na]
    at org.jooq.impl.AbstractRecord.from0(AbstractRecord.java:911) ~[jooq-3.15.3.jar:na]
    at org.jooq.impl.AbstractRecord.from(AbstractRecord.java:941) ~[jooq-3.15.3.jar:na]
    at org.jooq.impl.JSONReader.lambda$read$1(JSONReader.java:201) ~[jooq-3.15.3.jar:na]
    at org.jooq.impl.RecordDelegate.operate(RecordDelegate.java:143) ~[jooq-3.15.3.jar:na]
    at org.jooq.impl.JSONReader.read(JSONReader.java:200) ~[jooq-3.15.3.jar:na]
    at org.jooq.impl.JSONReader.read(JSONReader.java:110) ~[jooq-3.15.3.jar:na]
    at org.jooq.impl.DefaultBinding$DefaultResultBinding.readMultiset(DefaultBinding.java:3829) ~[jooq-3.15.3.jar:na]
    at org.jooq.impl.DefaultBinding$DefaultResultBinding.get0(DefaultBinding.java:3808) ~[jooq-3.15.3.jar:na]
    at org.jooq.impl.DefaultBinding$DefaultResultBinding.get0(DefaultBinding.java:3786) ~[jooq-3.15.3.jar:na]
    at org.jooq.impl.DefaultBinding$AbstractBinding.get(DefaultBinding.java:946) ~[jooq-3.15.3.jar:na]
    at org.jooq.impl.CursorImpl$CursorIterator$CursorRecordInitialiser.setValue(CursorImpl.java:1551) ~[jooq-3.15.3.jar:na]
    at org.jooq.impl.CursorImpl$CursorIterator$CursorRecordInitialiser.apply(CursorImpl.java:1500) ~[jooq-3.15.3.jar:na]
    at org.jooq.impl.CursorImpl$CursorIterator$CursorRecordInitialiser.apply(CursorImpl.java:1459) ~[jooq-3.15.3.jar:na]
    at org.jooq.impl.RecordDelegate.operate(RecordDelegate.java:143) ~[jooq-3.15.3.jar:na]
    at org.jooq.impl.CursorImpl$CursorIterator.fetchNext(CursorImpl.java:1424) ~[jooq-3.15.3.jar:na]
    at org.jooq.impl.CursorImpl$CursorIterator.hasNext(CursorImpl.java:1400) ~[jooq-3.15.3.jar:na]
    at java.base/java.util.Iterator.forEachRemaining(Iterator.java:132) ~[na:na]
    at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801) ~[na:na]
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484) ~[na:na]
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474) ~[na:na]
    at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913) ~[na:na]
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na]
    at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578) ~[na:na]
    at org.jooq.impl.AbstractCursor.collect(AbstractCursor.java:78) ~[jooq-3.15.3.jar:na]
    at org.jooq.impl.ResultQueryTrait.collect(ResultQueryTrait.java:358) ~[jooq-3.15.3.jar:na]
    at org.jooq.impl.ResultQueryTrait.fetchInto(ResultQueryTrait.java:1423) ~[jooq-3.15.3.jar:na]
    at package.MyProductDao.filterProducts(MyProductDao.java:129) ~[classes/:na]

查询有问题吗?或者这可能是与 jooq 基于 JSON 的 postgres 多集仿真有关的问题?

Java 11 Jooq 3.15.3 Postgres 驱动 42.2.24

感谢您的帮助!

亲切的问候, 安德烈亚斯

小编辑:如何按多集的大小对结果集进行排序?无论是实际大小,还是伪大小:将至少有一个存储的放在前面,然后将没有的放在后面。

【问题讨论】:

好的,关于通过多重集对结果集进行排序,可以使用orderBy(inline(5).desc())。这给出了预期的结果,即分配了 product_storage 的那些被放在第一位。如果有更多的选择会很有趣。 我建议单独问一个关于排序的问题,因为它与错误没有严格的关系。我们可以在那里讨论订购选项... 感谢您的回复。我在这里发布了另一个问题***.com/questions/69552492/… 好的,您假设内部错误导致查询失败? 【参考方案1】:

jOOQ 3.15.3 中似乎存在一个错误,它试图通过 Jackson 将 JSON 数据映射到 JSONJSONB,而不是在内部通过 jOOQ 自己的序列化器,请参阅:

https://github.com/jOOQ/jOOQ/issues/12508(与杰克逊有关的问题) https://github.com/jOOQ/jOOQ/issues/12509(DefaultConverterProvider 提供这种映射的能力)

该错误将在 3.16.0 和 3.15.4 中修复。作为一种解决方法,您可以暂时将您的 JSONB 文档转换为 TEXT

【讨论】:

感谢您的回复和提示,通过STORAGE_COORDINATE_INSTANCE.HIERARCHY.cast(String.class)选择允许获取层次结构!

以上是关于JOOQ用法和实例的主要内容,如果未能解决你的问题,请参考以下文章

jooq 和 java 8 流 SQL 生成

使用 JOOQ 在 java.sql.Timestamp 和 java.time.Instant 之间转换时遇到问题

使用 jOOQ 执行 PL/SQL 函数时的 Java 空指针

jOOQ(Java)-SQL 翻译 API 可以自托管吗?

Java JOOQ 与 SQL Server:缓存查询

jooq 比较 2 个日期 - 它不喜欢 java.sql.Date