phoenix7360
phoenix7360

Reputation: 2907

Spring Mongodb - Cannot write custom Converter for java.time.Period

I'm using Spring Cloud Brixton.SR4 with Spring Data MongoDB.

I have a very simple entity:

@Document
public class Foo{
    private Period period;

    //getter & setter
}

Because java.time.Period is not supported by jsr310 I'm creating custom converters:

class Converters {
    @Component
    @WritingConverter
    static class PeriodToStringConverter implements Converter<Period, String> {
        @Override
        public String convert(Period period) {
            return period.toString();
        }
    }

    @ReadingConverter
    @Component
    static class StringToPeriodConverter implements Converter<String, Period> {

        @Override
        public Period convert(String s) {
            return Period.parse(s);
        }
    }

Now I register them in my configuration class extending AbstractMongoConfiguration:

    @Bean
    @Override
    public MappingMongoConverter mappingMongoConverter() throws Exception {
        DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory());
        MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mongoMappingContext());
        final CustomConversions conversions = customConversions();
        log.info("hasCustomWriteTarget(Period.class): " + conversions.hasCustomWriteTarget(Period.class));
        log.info("hasCustomWriteTarget(Period.class, String.class): " + conversions.hasCustomWriteTarget(Period.class, String.class));
        log.info("hasCustomReadTarget(String.class, Period.class): " + conversions.hasCustomReadTarget(String.class, Period.class));
        converter.setCustomConversions(conversions);
        converter.afterPropertiesSet(); //probably not needed, trying out of despair
        return converter;
    }

    @Bean
    @Override
    public CustomConversions customConversions() {
        List<Converter> converters = new ArrayList<>();
        converters.add(new Converters.PeriodToStringConverter());
        converters.add(new Converters.StringToPeriodConverter());
        return new CustomConversions(converters);
    }

When I start my app I see in the logs:

hasCustomWriteTarget(Period.class): true
hasCustomWriteTarget(Period.class, String.class): true
hasCustomReadTarget(String.class, Period.class): true

Now I create a new Foo and save it to my repository:

Foo foo = new Foo();
foo.setPeriod(Period.of(2, 0, 1));
fooRepository.save(foo);

Now the weirdness happens:

In Mongodb I see:

{
  "_id": ObjectId("xxxx"),
  "period": {
      "years" : 0,
      "months" : 2,
      "days" : 1
    }
}

So already that's something wrong. It should be saved as a String

When I try to read the object in Java I get:

org.springframework.data.mapping.model.MappingException: No property null found on entity class java.time.Period to bind constructor parameter to!

I debugged the code in MappingMongoConverter:

    if (conversions.hasCustomReadTarget(dbo.getClass(), rawType)) {
        return conversionService.convert(dbo, rawType);
    }

because my object was not store as a String the dbo variable is actually a BasicDbObject and therefore I don't have converter for this.

Any idea why my write converter is not being used to persist the Period?

I have jackson-datatype-jdk8 on my classpath, could it be the issue? Would jackson be involved at all for persisting in Mongodb?

EDIT

It seems to be a registration issue. When I debug the code, the CustomConversion object used in MappingMongoConverter is different than the one I create. And it doesn't have the custom converters which I create

Upvotes: 1

Views: 2587

Answers (1)

phoenix7360
phoenix7360

Reputation: 2907

OK it was extremely stupid...

I was also creating my own MongoTemplate:

@Bean
public MongoTemplate mongoTemplate() throws Exception {
    return new MongoTemplate(mongoDbFactory());
}

Which basically ignores my custom converter. To fix it:

@Bean
public MongoTemplate mongoTemplate() throws Exception {
    return new MongoTemplate(mongoDbFactory(), mappingMongoConverter());
}

Upvotes: 1

Related Questions