dgaviola
dgaviola

Reputation: 2469

Prevent Spring Data for Mongo to convert ids to ObjectId

I'm using Spring Data for Mongo on an existing database. The previous application used plain strings for ids instead of ObjectId.

My problem is that Spring Data insists on converting the strings to ObjectId, which makes all queries by id to fail.

For example, when I do repository.findOne(''), the query executed is { "_id" : { "$oid" : "50cf9f34458cf91108ceb2b4"}} when it should be { "_id" : "50cf9f34458cf91108ceb2b4" }

Is there a way to avoid Spring Data to convert string ids to ObjectId?

Thanks!

Diego

Upvotes: 9

Views: 6660

Answers (7)

Onur Vuranok
Onur Vuranok

Reputation: 1

Somehow if you are not able to use annotations on the entity class then you can extend CachingMongoPersistentProperty to override isIdProperty() method. This will prevent converting "id" properties to "_id":

public class NotEditableEntityMongoPersistentProperty extends CachingMongoPersistentProperty {

  public NotEditableEntityMongoPersistentProperty(final Property property, final MongoPersistentEntity<?> owner,
      final SimpleTypeHolder simpleTypeHolder, final FieldNamingStrategy fieldNamingStrategy) {
    super(property, owner, simpleTypeHolder, fieldNamingStrategy);
  }

  @Override
  public boolean isIdProperty() {
    return false;
  }

}

Then use this on MongoMappingContext:

public class NotEditableEntityMongoMappingContext extends MongoMappingContext {
    
  @Override
  public MongoPersistentProperty createPersistentProperty(final Property property, final BasicMongoPersistentEntity<?> owner,
      final SimpleTypeHolder simpleTypeHolder) {
    return new NotEditableEntityMongoPersistentProperty(property, owner, simpleTypeHolder, PropertyNameFieldNamingStrategy.INSTANCE);
  }
}

Then

  @Bean
  public MongoTemplate mongoTemplate(@Autowired final MongoDatabaseFactory mongoDbFactory) {
    DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory);

    MongoMappingContext mappingContext = new NotEditableEntityMongoMappingContext();
    MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mappingContext);

    return new MongoTemplate(mongoDbFactory, converter);
  }

Upvotes: 0

Arun Girivasan
Arun Girivasan

Reputation: 602

Field annotation with FieldType=IMPLICIT worked for me

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.FieldType;

public class FilesInUploading {

    @Id
    @Field(targetType = FieldType.IMPLICIT)
    private String id;
...

}

I also tried with FieldType=STRING. But $nin, $in queries not worked for me at that time.

Upvotes: 0

Christian Pao.
Christian Pao.

Reputation: 543

You can use the Field annotation.

import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.FieldType;
import org.springframework.data.mongodb.core.mapping.Document;

@Document    
public class Dog{
@Id
@Field(targetType = FieldType.STRING)
String myId;
...
}

Upvotes: 3

Mostafa Omar
Mostafa Omar

Reputation: 177

I've found a simpler approach to the same one serhii took where you only override the method convertId in MobileMappingMongoConverter class, you'll find it in this post.

Upvotes: 0

serhii
serhii

Reputation: 71

now enough just override convertId method in MappingMongoConverter.

default here https://github.com/spring-projects/spring-data-mongodb/blob/master/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverter.java#L128. As we can see, conversionService will no so helpful.

that's part of my config (I don't need ObjectId from string at all in my collections):

@Configuration
public class MongoCommonConfig extends AbstractMongoClientConfiguration {
    ...
    @Bean
    @Primary
    public MongoTemplate mongoTemplate(MongoDbFactory dbFactory, 
        MappingMongoConverter mappingMongoConverter) {
        return new MongoTemplate(dbFactory, mappingMongoConverter);
    }

    @Bean
    @Primary
    @Override
    public @NotNull MappingMongoConverter mappingMongoConverter() throws Exception {
        DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory());
        MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mongoMappingContext()) {
            @Override
            public Object convertId(Object id, @NotNull Class<?> targetType) {
                return id;
            }
        };
        converter.setCustomConversions(customConversions());
        converter.setCodecRegistryProvider(mongoDbFactory());

        return converter;
    }
    ...
}

Upvotes: 1

Sergii Motynga
Sergii Motynga

Reputation: 21

I've faced same problem and my solution was like below:

  @Bean
  public MappingMongoConverter mappingMongoConverter(MongoDbFactory factory, MongoMappingContext context, CustomConversions conversions) {
    MappingMongoConverter converter = new MappingMongoConverter(new DefaultDbRefResolver(factory), context) {
      @Override
      public void afterPropertiesSet() {
        conversions.registerConvertersIn(conversionService);
      }
    };
    converter.setCustomConversions(conversions);
    return converter;
  }

The idea is to prevent default converters registration.

Upvotes: 1

dgaviola
dgaviola

Reputation: 2469

I finally found a solution for this. Probably not the best option, but works.

What I did was remove the converter from String to ObjectId that MongoTemplate uses through QueryMapper. This way, I created the following Mongo converter:

public class CustomMongoConverter extends MappingMongoConverter {
    public CustomMongoConverter(MongoDbFactory mongoDbFactory, MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext) {
        super(mongoDbFactory, mappingContext);
        conversionService.addConverter(new Converter<String, ObjectId>() {
            @Override
            public ObjectId convert(String source) {
                throw new RuntimeException();
            }
        });
    }
}

And then, I passed that implementation of the converter to MongoTemplate:

<bean id="mongoConverter" class="com.abcompany.model.repositories.utils.CustomMongoConverter">
    <constructor-arg ref="mongoDbFactory"/>
    <constructor-arg>
        <bean class="org.springframework.data.mongodb.core.mapping.MongoMappingContext"/>
    </constructor-arg>
</bean>

<bean class="org.springframework.data.mongodb.core.MongoTemplate" id="mongoTemplate">
    <constructor-arg ref="mongoDbFactory"/>
    <constructor-arg ref="mongoConverter"/>
</bean>

This way, when trying to convert from String to ObjectId, it throws an exception and it doesn't do it. Please note that you probably can just remove the converter from conversionService.

Upvotes: 1

Related Questions