简体   繁体   中英

Save and load Guava Optional<?>s in MongoDB using Spring-Data

How to tune the mapping of the Guava Optional s (or later the JDK8 Optionals) with Spring-Data-MongoDb?

As an example the following class should be mapped like the json below.

@Data
public class Test
{
    Optional<String> stringOptionalNull    = null;
    Optional<String> stringOptionalAbsent  = Optional.absent();
    Optional<String> stringOptionalPresent = Optional.of("ExampleValue");   
}

Json (Note: The Null and absent cases are treated in the same manner):

{
   "stringOptionalPresent" : "ExampleValue"
}

When they are converted back to java again, the null and absent should be translated to Optional.absent() .

From the Spring Data MongoDB Docs I read that the org.springframework...Converter s are the way to go. But with the API I was not able to treat the null cases correct.

The is a complete testcase to reproduce the problem. It hands in a null and tests that the loaded object contains only Optional.absent() .

public class OptionalMongoDbTest
{
    @Data
    @AllArgsConstructor
    static class ClassWithOptionals
    {
        private final Optional<String> stringOptionalNull;
        private final Optional<String> stringOptionalPresent;
        private final Optional<String> stringOptionalAbsent;
    }

    public static class OptionalToStringConverter<T> implements Converter<Optional<T>, String>
    {
        @Override
        public String convert(final Optional<T> arg0)
        {
            return arg0.isPresent() ? arg0.get().toString() : null;
        }
    }

    public static class StringToOptionalConverter implements Converter<String, Optional<String>>
    {
        @Override
        public Optional<String> convert(final String arg0)
        {
            return Optional.fromNullable(arg0);
        }
    }

    @Test
    public void persisted_object_should_be_equal() throws Exception
    {
        // Setup Mongodb connection
        final SimpleMongoDbFactory mongoDbFactory = new SimpleMongoDbFactory(new Mongo(), "test");
        final MappingMongoConverter converter = new MappingMongoConverter(mongoDbFactory, new MongoMappingContext());

        // Register Custom Converter
        converter.setCustomConversions(new CustomConversions(Arrays.asList(new OptionalToStringConverter(), new StringToOptionalConverter())));
        converter.afterPropertiesSet();

        final MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory, converter);
        mongoTemplate.setWriteConcern(WriteConcern.SAFE);

        // Test the mapping
        mongoTemplate.dropCollection(ClassWithOptionals.class);

        final ClassWithOptionals objectToSave = new ClassWithOptionals(null, Optional.of("ExampleValue"), Optional.<String> absent());
        mongoTemplate.save(objectToSave);

        final ClassWithOptionals objectFromMongo = mongoTemplate.findAll(ClassWithOptionals.class).get(0);

        // TODO: That test fails because there are null values comming from the databases
        assertThat(objectFromMongo, is(new ClassWithOptionals(Optional.<String> absent(), Optional.of("ExampleValue"), Optional.<String> absent())));
    }
}

Furthermore the json generated by this test looks like this:

{
    "_id" : ObjectId("5343d8a5e4b0215d78a322d0"),
    "stringOptionalPresent" : "ExampleValue",
    "stringOptionalAbsent" : null // this line should be removed.
}

Edit

I think that is not possible because spring data mongo doesn't trigger custom conversions on null objects. That is because of this: https://github.com/spring-projects/spring-data-mongodb/blob/master/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java#L1037-L1041

I don't use Spring so I don't know whether this will work for you but try and make your setter work "normally" and transform your getter to this:

public Optional<String> getFoo()
{
    return Optional.fromNullable(foo);
}

If foo is null , it will be an Optional.absent() for which .isPresent() will return false .

In Java 8 (which has "stolen" Optional from Guava) the API is a little different; it is not Optional.absent() but Optional.empty() , and .fromNullable() has become .ofNullable() .

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