将包含数组的JSONB映射为具有JOQ的POJO列表。

huangapple go评论73阅读模式
英文:

Map a JSONB containing an array to a list of POJO with JOOQ

问题

我有一个包含以下数据的JSONB列

    '
                [
                  {
                    "date": X1,
                    "value": "Y1"
                  },
                  {
                    "date": X2,
                    "value": "Y2"
                  },
                  {
                    "date": X3,
                    "value": "Y3"
                  }
                ]
            '

我想要提取它并将其转换为一个Pojos列表其中Pojos如下

    public record Point(LocalDate date, Double value)

这是我目前正在做的

    public Mono<List<Point>> getPoints(int id) {
            return Mono.from(
                    dsl.select(MY_TABLE.POINTS)
                        .from(MY_TABLE)
                        .where(MY_TABLE.ID.eq(id)))
                .map(record -> {
                    try {
                        return Arrays.stream(objectMapper.readValue(record.component1().data(), Point[].class)).toList();
                    } catch (JsonProcessingException e) {
                        throw new RuntimeException(e);
                    }
                });
        }

我想知道是否有更简洁的解决方案也许可以使用默认的Jooq映射器将记录映射到Point列表我尝试创建一个包装类

    public record PointWrapper(List<Point> points)

然后使用

    .map(record -> record.into(PointWrapper.class))

但我得到的是一个包含`LinkedHashMap`的列表在运行时当我从列表中提取一个元素用作`Point`时会产生`ClassCastException`。
英文:

I've the following JSONB column containing this data:

&#39;
[
{
&quot;date&quot;: X1,
&quot;value&quot;: &quot;Y1&quot;
},
{
&quot;date&quot;: X2,
&quot;value&quot;: &quot;Y2&quot;
},
{
&quot;date&quot;: X3,
&quot;value&quot;: &quot;Y3&quot;
}
]
&#39;

And I want to fetch it and convert it to a list of Pojos, where the Pojo is:

public record Point(LocalDate date, Double value)

This is what I'm doing now:

public Mono&lt;List&lt;Point&gt;&gt; getPoints(int id) {
return Mono.from(
dsl.select(MY_TABLE.POINTS)
.from(MY_TABLE)
.where(MY_TABLE.ID.eq(id)))
.map(record -&gt; {
try {
return Arrays.stream(objectMapper.readValue(record.component1().data(), Point[].class)).toList();
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
});
}

I was wondering if there is a cleaner solution, maybe using the default Jooq mapper to map the record to a List of Points. I tried to create a Wrapper:

public record PointWrapper(List&lt;Point&gt; points)

and use:

.map(record -&gt; record.into(PointWrapper.class))

but what I get is a List of LinkedHashMap which of course generate a ClassCastException at runtime when I extract an element from the list to be used as Point.

答案1

得分: 1

以下是翻译好的内容:

将Jackson转换器附加到您的列上

jOOQ的代码生成器支持"Jackson转换器",即“Jackson感知的”转换器,以便在JSON(B)和自定义数据类型之间进行转换。

依赖于jOOQ的DefaultConverterProvider

JSON(B)和自定义数据类型之间进行转换,并且当jOOQ在您的类路径中找到Jackson(或Gson)时,它将尝试在数据类型之间进行映射。请参阅解释此部分。 这可能是您已经获得List&lt;Map&lt;...&gt;&gt;的原因。

为什么它对您不起作用

以上两种方法都只是对您已经做过的事情的语法糖,即从JSONB.data()中提取String内容,因为Jackson不知道jOOQ的JSONB数据类型。一旦您到达那里,您必须按照Jackson的映射规则来进行映射以能够在类型之间进行映射。

请注意,在您的显式示例中,您正在映射JSON数组和Point[]之间的对应关系是明显的。在您的PointWrapper示例中,您使用的是List&lt;Point&gt;,而jOOQ的DefaultRecordMapper(尝试将Record1&lt;T&gt;映射到单参数构造函数调用)不知道要从Jackson中获取List(原始类型)以外的内容,只能使用默认行为。如果有一个PointWrapper(Point[]),那么它可能会再次工作。

无论如何,使用反射的DefaultRecordMapper具有其局限性,包括Java中的类型擦除所带来的限制。某些事情可能会得到改进,但总的来说,最好是明确且类型安全。我只是附加了某种类型的Converter到列上。这样,您始终可以获取您喜欢的用户定义类型,并且您永远不必再考虑数据类型转换,更不用担心由类型不安全的反射引起的一些怪癖。

英文:

There are several ways of using jOOQ's out of the box Jackson support:

Attaching a Jackson converter to your column

jOOQ's code generator supports "Jackson converters", i.e. converters that are "Jackson aware", in order to convert between JSON(B) and custom data types.

Relying on jOOQ's DefaultConverterProvider

When converting between JSON(B) and a custom data type, and when jOOQ finds Jackson (or Gson) on your classpath, then it will attempt to map between the data types. See this section that explains it. This is probably why you already got a List&lt;Map&lt;...&gt;&gt;

Why it didn't work for you

Both of the above approaches are just syntax sugar for what you already did, namely to extract the String content from JSONB.data(), because Jackson doesn't know about jOOQ's JSONB data type. Once you're there, you have to follow the mapping rules by Jackson to be able to map between types.

Notice how in your explicit example, you're mapping between the JSON array and a Point[], which have an obvious correspondence. In your PointWrapper example, you're using List&lt;Point&gt;, and jOOQ's DefaultRecordMapper (which tries to map a Record1&lt;T&gt; to a single-argument constructor call), doesn't know what to do request from Jackson given the List (rawtype) other than just the default behaviour. If you had a PointWrapper(Point[]), then it would probably work again.

In any case, the DefaultRecordMapper using reflection has its limitations, including those imposed by type erasure in Java. Some things could be improved, but in general, it's often better to be explicit and type safe. I'd just attach a Converter of some sort to the column. That way, you can always fetch the user defined type, which you prefer, and you never have to think about data type conversion again, let alone some quirks caused by type-unsafe reflection.

huangapple
  • 本文由 发表于 2023年7月17日 18:35:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/76703623.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定