如何在MongoDB中保存一个java.time.Instant并无异常地加载相同的值? [英] How to save a java.time.Instant in mongodb and load the same value out without exception?

查看:42
本文介绍了如何在MongoDB中保存一个java.time.Instant并无异常地加载相同的值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望java.time.Instant在传入和传出mongo时具有相同的值:

data class Person(@Id val name: String, val born: Instant)

@ExtendWith(SpringExtension::class)
@SpringBootTest
class MongoMappingTest(@Autowired private val mongoTemplate: MongoTemplate)
{
    @Test
    fun `Test custom mapping of the Instant data type`()
    {
        testFor(Instant.MAX)   // This throws java.lang.ArithmeticException
                               // Failed to convert from type [java.time.Instant] to type [java.util.Date]

        testFor(Instant.now()) // This fails with:
                               // Expected :Person(name=Jim, born=2021-04-07T16:56:10.838Z)
                               // Actual   :Person(name=Jim, born=2021-04-07T16:56:10.838228297Z)
    }

    fun testFor(instant: Instant)
    {
        val jim = Person("Jim", instant)
        mongoTemplate.save(jim)
        val jimOut = mongoTemplate.findById("Jim", Person::class.java)
        Assertions.assertThat(jim).isEqualTo(jimOut)
    }
}
Instant首先被转换为java.util.Date(不知道为什么会发生这种情况),而后者又被转换为BSON Date数据类型。后者只有几毫秒的精度,所以我们需要放弃它。我制作了自己的表示,我正在尝试替换它(在docs之后):

data class InstantRepresentation
(
    val seconds: Long, val nanoseconds: Int
)

class InstantWriteConverter: Converter<Instant, InstantRepresentation>
{
    override fun convert(source: Instant): InstantRepresentation
    {
        return InstantRepresentation(source.epochSecond, source.nano)
    }
}

class InstantReadConverter: Converter<InstantRepresentation, Instant>
{
    override fun convert(source: InstantRepresentation): Instant?
    {
        return Instant.ofEpochSecond (source.seconds, source.nanoseconds.toLong())
    }
}

@Configuration
class MongoConfig: AbstractMongoClientConfiguration()
{
    public override fun getDatabaseName(): String
    {
        return "db-test"
    }


    public override fun configureConverters(adapter: MongoConverterConfigurationAdapter)
    {
        adapter.registerConverter(InstantWriteConverter())
        adapter.registerConverter(InstantReadConverter())
    }
}

然而,一切都没有改变。我的转换代码未被提取。似乎Spring仍在尝试将我的Instant转换为java.util.Date

我尝试使用@Field(targetType = FieldType.IMPLICIT)Person.born进行批注,以告知系统我希望将其存储为对象,但无济于事。

推荐答案

解决方案:

val secondsFieldName = "seconds"
val nanoSecondsFieldname = "nanoseconds"

@WritingConverter
class InstantWriteConverter: Converter<Instant, Document>
{
    override fun convert(source: Instant): Document
    {
        return Document(mapOf(
                secondsFieldName to source.epochSecond, 
                nanoSecondsFieldname to source.nano.toLong()))
    }
}

@ReadingConverter
class InstantReadConverter: Converter<Document, Instant>
{
    override fun convert(source: Document): Instant
    {
        return Instant.ofEpochSecond (
                source[secondsFieldName] as Long, 
                source[nanoSecondsFieldname] as Long)
    }
}

@Configuration
class MongoConfig
{
    @Bean
    fun customConversions() = MongoCustomConversions(listOf(
               InstantWriteConverter(), 
               InstantReadConverter()))
}

主要内容:

您不能将您的类型转换为您喜欢的任何类型。org.bson.Document有效。其他一些类也可以工作,但不清楚您应该使用哪些类或哪些类。

这篇关于如何在MongoDB中保存一个java.time.Instant并无异常地加载相同的值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆