简体   繁体   English

使用 Jooq 反序列化 pojo object 时出错

[英]Error in deserializing pojo object with Jooq

I am facing a strange, at least for me, deserialization problem using Jooq.我面临着一个奇怪的,至少对我来说,使用 Jooq 的反序列化问题。 I have a POJO, a pretty simple one (with some custom Deserilizers but not related to this problem):我有一个 POJO,一个非常简单的(带有一些自定义 Deserilizers 但与此问题无关):

@Builder
@Value
@Jacksonized
@JsonIgnoreProperties(ignoreUnknown = true)
public class AlertVector implements Serializable {

    @JsonProperty(ERROR_ID)
    @Column(name = ALERTS_ERROR_ID_COLUMN, nullable=false)
    private long alertVectorId;

    /**
     * The GTM instant of the Alert log.
     */
    @JsonProperty(ERROR_TIMESTAMP)
    @Column(name = ALERTS_ERROR_TIMESTAMP_COLUMN)
    private Instant alertVectorTimestamp;
    
    /**
     * Machine Serial Number
     */
    @JsonProperty(MACHINE_SERIAL_NUMBER)
    @Column(name = ALERTS_MACHINE_SERIAL_NUMBER_COLUMN)
    @Builder.Default private int machineSerialNumber = -1;

    /**
     * Status of the record 
     * (i.e. the data are sent or not to ES)
     */
    @JsonIgnore
    @Column(name = ALERTS_RECORD_STATUS_COLUMN)
    private int recordStatus;

        /**
     * Array of Safety Faults bits
     */
    @JsonDeserialize(using = ByteArrayDeserializer.class)
    @JsonSerialize(using = ByteArraySerializer.class)
    @JsonProperty(SAFETY_FAULTS_NAME)
    @Column(name = ALERTS_SAFETY_FAULTS_COLUMN)
    private byte[] safetyFaults;

    /**
     * Array of Errors bits
     */
    @JsonDeserialize(using = ByteArrayDeserializer.class)
    @JsonSerialize(using = ByteArraySerializer.class)
    @JsonProperty(ERRORS_NAME)
    @Column(name = ALERTS_ERRORS_COLUMN)
    private byte[] errors;
    
    /**
     * Array of Warnings bits
     */
    @JsonDeserialize(using = ByteArrayDeserializer.class)
    @JsonSerialize(using = ByteArraySerializer.class)
    @JsonProperty(WARNINGS_NAME)
    @Column(name = ALERTS_WARNINGS_COLUMN)
    private byte[] warnings;
}

I build a test database with H2 to write some Junit tests.我用 H2 构建了一个测试数据库来编写一些 Junit 测试。 The test databases is fed with 24 records of data with:测试数据库包含 24 条数据记录,其中:

URI alertURI = getClass().getClassLoader().getResource("alerts.csv").toURI();
String strAlerts = Files.readString(Path.of(alertURI), StandardCharsets.US_ASCII);
Result<AlertsRecord> alertsRecordsResult = context.fetchFromCSV(strAlerts).into(it.fox.mysql.tables.Alerts.ALERTS);
context.insertInto(ALERTS).columns(ALERTS.fields()).valuesOfRecords(alertsRecordsResult).execute();

I have checked that the data are correctly ingested in the DB like I expect reading back a record and visually checking it:我已经检查了数据库中的数据是否正确摄取,就像我希望读回记录并目视检查它一样:

context.select(ALERTS.asterisk()).from(ALERTS).where(ALERTS.ERRORID.eq(1661367543364l)).fetch()
"+-------------+--------------------+------------+-------------------+----------------------------------------+----------------------------+----------------------------+
|      errorId|errorTimestamp      |recordStatus|machineSerialNumber|safetyFaults                            |errors                      |warnings                    |
+-------------+--------------------+------------+-------------------+----------------------------------------+----------------------------+----------------------------+
|1661367543364|2022-08-24T18:59:03Z|           0|                  5|AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA|AAAAAAAAAAAAAAAAAAAAAAAAAAA=|AAAAAAAAAAAAAAAxAAAAAAAAAAA=|
+-------------+--------------------+------------+-------------------+----------------------------------------+----------------------------+----------------------------+

The strange thing I found is that, even if the data are correct in the DataBase, the deserialization of my POJO is wrong:我发现奇怪的是,即使DataBase中的数据是正确的,我的POJO的反序列化也是错误的:

context.select().from(ALERTS).where(ALERTS.ERRORID.eq(1661367543364l)).fetchOneInto(AlertVector.class);

The returned POJO has the filed machineSerialNumber and recordStatus inverted:返回的 POJO 将machineSerialNumberrecordStatus反转:

AlertVector@111 "AlertVector(alertVectorId=1661367536365, alertVectorTimestamp=2022-08-24T18:58:56Z, machineSerialNumber=0, recordStatus=5, safetyFaults=[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], errors=[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49], warnings=[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])"

If I modify the POJO "inverting" the position of the two variables like this:如果我修改 POJO“反转”两个变量的 position,如下所示:

/**
 * Status of the record 
 * (i.e. the data are sent or not to ES)
 */
@JsonIgnore
@Column(name = ALERTS_RECORD_STATUS_COLUMN)
private int recordStatus;

/**
 * Machine Serial Number
 */
@JsonProperty(MACHINE_SERIAL_NUMBER)
@Column(name = ALERTS_MACHINE_SERIAL_NUMBER_COLUMN)
@Builder.Default private int machineSerialNumber = -1;

The same code give me a correct AlertVector POJO.相同的代码给了我一个正确的AlertVector POJO。

I build the Jooq classes from XML and I have noted that I wrote the definition of recordStatus before the definition of machineSerialNumber :我从 XML 构建了 Jooq 类,我注意到我在machineSerialNumber的定义之前编写了recordStatus的定义:

        <column>
            <table_catalog></table_catalog>
            <table_schema>plc_data</table_schema>
            <table_name>alerts</table_name>
            <column_name>recordStatus</column_name>
            <data_type>int(11)</data_type>
            <character_maximum_length>0</character_maximum_length>
            <numeric_precision>10</numeric_precision>
            <numeric_scale>0</numeric_scale>
            <ordinal_position>3</ordinal_position>
            <is_nullable>true</is_nullable>
            <comment></comment>
        </column>
        <column>
            <table_catalog></table_catalog>
            <table_schema>plc_data</table_schema>
            <table_name>alerts</table_name>
            <column_name>machineSerialNumber</column_name>
            <data_type>int(11)</data_type>
            <character_maximum_length>0</character_maximum_length>
            <numeric_precision>10</numeric_precision>
            <numeric_scale>0</numeric_scale>
            <ordinal_position>4</ordinal_position>
            <is_nullable>true</is_nullable>
            <column_default>NULL</column_default>
            <comment></comment>
        </column>

Could someone explain me the behaviour here?有人可以解释一下这里的行为吗? The XML column definition MUST be in the very same order of the one in POJO class? XML 列定义必须与 POJO class 中的顺序完全相同?

Thanks, S.谢谢,S。

DefaultRecordMapper vs DefaultConverterProvider DefaultRecordMapperDefaultConverterProvider

The XML column definition MUST be in the very same order of the one in POJO class? XML 列定义必须与 POJO class 中的顺序完全相同?

It's obviously recommended that you match the order of columns from the database also in generated code, because otherwise, you'll run into problems like these.显然建议您在生成的代码中也匹配数据库中列的顺序,否则,您将遇到此类问题。

But it isn't strictly required, nor is your problem strictly related to code generation.但这不是严格要求的,您的问题也不是与代码生成严格相关的。 It's rather caused by the behaviour of the DefaultRecordMapper .它是由DefaultRecordMapper的行为引起的。 You say:你说:

I have a POJO, a pretty simple one我有一个 POJO,一个非常简单的

But it isn't simple.但这并不简单。 You're mixing Jackson and this @Builder annotation stuff, which both compete for DefaultRecordMapper behaviour.您正在混合 Jackson 和这个@Builder注释东西,它们都竞争DefaultRecordMapper行为。 You're probably thinking that Jackson should kick in, mapping database column names to attributes, but that's not the case.您可能认为 Jackson 应该启动,将数据库列名映射到属性,但事实并非如此。 The Jackson mapping logic is implemented in the ConverterProvider default implementation, when mapping an individual JSON value to a POJO.当将单个 JSON 值映射到 POJO 时,Jackson 映射逻辑在ConverterProvider默认实现中实现。 In your case, that doesn't apply.在你的情况下,这不适用。

DefaultRecordMapper and constructors DefaultRecordMapper和构造函数

Instead, the record is mapped to the POJO by jOOQ's DefaultRecordMapper , and your @Builder and @Value annotations probably produce a constructor with fields in the wrong order, given your SELECT * doesn't match your XML column definition.相反,记录由 jOOQ 的DefaultRecordMapper映射到 POJO,并且您的@Builder@Value注释可能会生成一个带有错误顺序的字段的构造函数,因为您的SELECT *与您的 XML 列定义不匹配。 But Java constructors, like any Java methods, don't retain their parameter names, so jOOQ can only map by parameter index, which doesn't match in your case.但是 Java 构造函数,像任何 Java 方法一样,不保留它们的参数名称,所以 jOOQ 只能通过参数索引匹配 map,这不匹配。

The real problem: Usage of explicit SELECT * in jOOQ真正的问题:在 jOOQ 中使用显式SELECT *

So, the main problem here is that you're explicitly using the asterisk() (or * ), effectively delegating the projection to the effective database schema, rather than using the default projection by jOOQ, which corresponds to that of the generated code.因此,这里的主要问题是您显式使用asterisk() (或* ),有效地将投影委托给有效的数据库模式,而不是使用 jOOQ 的默认投影,这对应于生成的代码。 The asterisk() was added explicitly to allow for using this feature, for whatever reason.无论出于何种原因,明确添加了asterisk()以允许使用此功能。 There's hardly any reason why users should use asterisk() , unless they want this from jOOQ.用户几乎没有任何理由应该使用asterisk() ,除非他们从 jOOQ 中获得它。 Better just write:最好只写:

context.selectFrom(ALERTS).where(ALERTS.ERRORID.eq(1661367543364l)).fetch()

This is also documented in the manual , or the Javadoc . 这也记录在手册Javadoc中。

Whenever jOOQ generates an asterisk (explicitly, or because jOOQ doesn't know the exact projection), the column order, and the column set are defined by the database server, not jOOQ.每当 jOOQ 生成星号(明确地,或者因为 jOOQ 不知道确切的投影)时,列顺序和列集由数据库服务器定义,而不是 jOOQ。 If you're using generated code, this may lead to problems as there might be a different column order than expected, as well as too many or too few columns might be projected.如果您使用生成的代码,这可能会导致问题,因为列顺序可能与预期不同,并且可能会投影太多或太少的列。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM