代码之家  ›  专栏  ›  技术社区  ›  Drazev

如何正确配置Spring引导以使用java.time.ZonedDateTime与postgresql进行持久化?

  •  0
  • Drazev  · 技术社区  · 3 年前

    我正在升级现有的微服务,并用一些新功能扩展数据库。

    我将程序升级为使用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支持分区时间表
    • 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-jsr310,那么spring应该自动配置jackson对象映射器以使用该模块。如果可能的话,我希望使用这种行为,而不是通过其他方式自定义对象映射器。

    我查找了一些可能的解决方案并进行了尝试。我试过的是

    1. 添加 com.fasterxml.jackson.datatype:jackson-datatype-jsr310 依赖
    2. 添加 @TimeZoneStorage 到 @Entity 使用的属性上的类 分区日期时间
    3. 正在添加 spring.jackson.serialization.write_dates_as_timestamps=true 到 application.properties

    似乎没有一个解决方案能解决这个错误。我对解决方案(2)持怀疑态度,因为我怀疑它被Hibernate 6的行为所取代,在Hibernate 6中,它会根据数据库和java类型自动选择正确的转换。解决方案(3)也让我感到怀疑,因为它似乎正在定义一种行为,该行为现在有望通过hibernate 6实现自动化,并可能导致问题。

    0 回复  |  直到 3 年前
        1
  •  0
  •   Drazev    3 年前

    在进一步调试之后,我确认spring确实将新模块加载到ObjectMapper中。

    这让我发现问题实际上来自另一个为进行日志记录而创建的对象映射器。我通过让它从spring上下文自动连接ObjectMapper实例而不是创建本地版本来解决这个问题。或者,您可以让它通过本地实例的生成器加载模块。

    我还发现了一个有趣的结论。

    jackson-datatype-jsr310模块现在被认为是遗留的。JavaTime模块包含在Jackson核心中,但没有自动注册。这可以在官方文件中找到 here 。

    注意,从2.6开始,这个模块不支持自动注册,因为存在遗留版本JSR310Module。旧版本具有相同的功能,但默认配置略有不同:有关详细信息,请参阅JSR310Module。