I would like a java.time.Instant to have the same value going in and out of 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)
}
}
The Instant first gets convereted to java.util.Date (no idea why this is happening), which in turn gets converted to the BSON Date data type. The latter has only milisecond precision, so we need to abandon it. I made my own representation and I am trying to substitute it (following the 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())
}
}
However, nothing has changed. My conversion code is not picked up. It seems that spring is still trying to convert my Instant into a java.util.Date.
I tried to annotate Person.born with @Field(targetType = FieldType.IMPLICIT) to tell the system that I want to store it as an object, but it didn't help.