简体   繁体   中英

Register a new Date Converter Auditable in Spring Data MongoDB for ZonedDateTime

I want my auditable ( @CreatedDate and @LastModifiedDate ) MongoDB document to work with ZonedDateTime fields.

Apparently this type is not supported by Spring Data (have a look at org.springframework.data.auditing.AnnotationAuditingMetadata ).

Framework version: Spring Boot 2.0.0 and Spring Data MongoDB 2.0.0

Spring Data auditing error:

java.lang.IllegalArgumentException: Invalid date type for member <MEMBER NAME>!
Supported types are [org.joda.time.DateTime, org.joda.time.LocalDateTime, java.util.Date, java.lang.Long, long].

Mongo configuration:

@Configuration
@EnableMongoAuditing
public class MongoConfiguration {

}

The auditable entity:

public abstract class BaseDocument {

    @CreatedDate
    private ZonedDateTime createdDate;

    @LastModifiedDate
    private ZonedDateTime lastModifiedDate;

}

Things I tried

I also tried creating a custom converter for ZonedDateTime , but it is not considered by Spring Data. The class DateConvertingAuditableBeanWrapper has a ConversionService which is configured in the constructor method with JodaTimeConverters , Jsr310Converters and ThreeTenBackPortConverters .

Custom converter:

@Component
public class LocalDateTimeToZonedDateTimeConverter implements Converter<LocalDateTime, ZonedDateTime> {

    @Override
    public ZonedDateTime convert(LocalDateTime source) {
        return source.atZone(ZoneId.systemDefault());
    }

}

Spring Data DateConvertingAuditableBeanWrapper:

class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory {

    abstract static class DateConvertingAuditableBeanWrapper implements AuditableBeanWrapper {

        private final ConversionService conversionService;

    }
}

Is it possible to audit ZonedDateTime fields?

How can I register a converter?

Create a DateTimeProvider to provide the current time to be used when auditing:

@Component("dateTimeProvider")
public class CustomDateTimeProvider implements DateTimeProvider {

    @Override
    public Optional<TemporalAccessor> getNow() {
        return Optional.of(ZonedDateTime.now());
    }
}

And then:

@Configuration
@EnableMongoAuditing(dateTimeProviderRef = "dateTimeProvider")
public class MongoConfiguration {

    @Bean
    public MongoCustomConversions customConversions() {
        List<Converter<?, ?>> converters = new ArrayList<>();
        converters.add(new DateToZonedDateTimeConverter());
        converters.add(new ZonedDateTimeToDateConverter());
        return new MongoCustomConversions(converters);
    }

    class DateToZonedDateTimeConverter implements Converter<Date, ZonedDateTime> {

        @Override
        public ZonedDateTime convert(Date source) {
            return source == null ? null : 
                    ZonedDateTime.ofInstant(source.toInstant(), ZoneId.systemDefault());
        }
    }

    class ZonedDateTimeToDateConverter implements Converter<ZonedDateTime, Date> {

        @Override
        public Date convert(ZonedDateTime source) {
            return source == null ? null : Date.from(source.toInstant());
        }
    }
}

I wouldn't, however, use ZonedDateTime for this purpose. I would stick to OffsetDateTime :

OffsetDateTime , ZonedDateTime and Instant all store an instant on the time-line to nanosecond precision. Instant is the simplest, simply representing the instant. OffsetDateTime adds to the instant the offset from UTC/Greenwich, which allows the local date-time to be obtained. ZonedDateTime adds full time-zone rules.

It is intended that ZonedDateTime or Instant is used to model data in simpler applications. This class may be used when modeling date-time concepts in more detail, or when communicating to a database or in a network protocol.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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