hibernate 如何正确配置Sping Boot 以使用java.time.ZonedDateTime来持久化postgresql?

fkaflof6  于 2023-08-06  发布在  Java
关注(0)|答案(1)|浏览(84)

我正在升级现有的微服务,并使用一些新功能扩展数据库。
我将程序升级为使用Spring 3.0.6,它使用Hibernate 6.1.7。我使用的数据库是postgresl testcontainers 1.18.1。
当我试图运行我的程序时,它在试图将数据持久化在数据库中时崩溃。它提供的错误如下…

java.lang.RuntimeException: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type `java.time.ZonedDateTime` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling ...

字符串
当我调查这个问题时我确实证实了这一点

  • Postgresql支持Zoned Timestmaps
  • Hibernate 6支持ZonedDateTime

以下是项目的一些片段。
pom.xml

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.6</version>
    </parent>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>       
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.ldap</groupId>
            <artifactId>spring-ldap-core</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-ldap</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-csv</artifactId>
        </dependency>

        <!--    Java 8 Date/time    -->
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
        </dependency>
@Entity
@Table(name="billable_Data",schema="mySchema")
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class BillableMetric {

    @Id
    UUID guid;

    @ManyToOne(fetch = FetchType.LAZY)
    CustomerInvoice invoice;

    @ManyToOne(fetch = FetchType.LAZY)
    CustomerRecord billingAccount;

    @OneToOne(fetch = FetchType.LAZY)
    CatalogueItem skuItem;

    @Column(name="dataSourceIdentifier")
    BillableMetricDataSource dataSource;

    Double quantity;

    String unitType;

    ZonedDateTime startTimeInclusive;

    ZonedDateTime endTimeExclusive;
}

的数据
表架构

CREATE TABLE mySchema.billable_Data (
    guid uuid  NOT NULL DEFAULT mySchema.gen_random_uuid(),
    invoice bigserial,
    billingAccount uuid, -- Can be null since one possible error is the billing account cannot be found
    skuItem bigserial, -- Can be null since one possible error is the sku is not valid.
    dataSource numeric NOT NULL, --Should be an enum that is associated to a MetricReading Table
    quantity numeric,
    unitType varchar(100),
    startTimeInclusive timestamp ,
    endTimeExclusive timestamp ,
    CONSTRAINT guid_is_pk PRIMARY KEY (guid),
    CONSTRAINT invoice_is_fk FOREIGN KEY (invoice) REFERENCES mySchema.customer_invoice(id),
    CONSTRAINT billable_data_sku_is_fk FOREIGN KEY (skuItem) REFERENCES mySchema.catalog_item(id),
    CONSTRAINT billable_data_billing_account_is_fk FOREIGN KEY (billingAccount)  REFERENCES mySchema.customer(guid)
);


我读到的是,如果我将jackson-datatype-jsr 310包含到项目中,那么spring应该自动配置jackson对象Map器来使用该模块。如果可能的话,我希望使用这种行为,而不是通过其他方式自定义对象Map器。
我找到了一些可能的解决方案并尝试了一下。我尝试的方法是
1.添加com.fasterxml.jackson.datatype:jackson-datatype-jsr310依赖项
1.将@TimeZoneStorage添加到使用ZonedDateTime的属性上的@Entity
1.将spring.jackson.serialization.write_dates_as_timestamps=true添加到application.properties
所有解决方案似乎都无法解决该错误。我对解决方案(2)持怀疑态度,因为我怀疑它被Hibernate 6的行为所取代,它会根据数据库和Java类型自动选择正确的转换。解决方案(3)也让我感到怀疑,因为它似乎定义了一个行为,而这个行为现在被期望在hibernate 6中自动化,并且可能会导致问题。

atmip9wb

atmip9wb1#

在进一步调试之后,我确认spring确实将新模块加载到ObjectMapper中。
这使我发现问题实际上是由另一个对象Map器引起的,该对象Map器是为了进行一些日志记录而创建的。我通过让它从spring上下文自动连接ObjectMapper示例而不是创建本地版本来解决这个问题。或者,您可以让它通过本地示例的构建器加载模块。
我还发现了一个有趣的结论。
jackson-datatype-jsr 310模块现在被认为是遗留的。JavaTime模块包含在Jackson核心中,但未自动注册。这可以在官方文档here中找到。
请注意,从2.6开始,该模块不支持自动注册,因为存在遗留版本JSR 310 Module。旧版本具有相同的功能,但默认配置略有不同:详情请参见JSR 310模块。

相关问题