如何在没有 ObjectId 的情况下将 Mongo 与 Spring Data 一起使用

Posted

技术标签:

【中文标题】如何在没有 ObjectId 的情况下将 Mongo 与 Spring Data 一起使用【英文标题】:How to use Mongo with Spring Data without ObjectId 【发布时间】:2016-02-21 21:32:39 【问题描述】:

我有一个现有的 mongo 数据库,其中 id (_id) 被持久化为纯字符串。这是 Mongo DB 中的示例数据:


    "_id" : "528bb0e2e4b0442f1479f1b4",
    "schoolId" : "URDLKYLFJXLWJGR193778316742298",
    "surname" : "Lewis",
    "admissionNumber" : "0347",
    "firstName" : "Martins"

我有一个 Java 对象,其形式为:

public class Student 

@Id
private String id;
private String schoolId;
private String surname;
private String admissionNumber;
private String firstName;

使用 Getter 和 Setter

我也有一个仓库:

public interface StudentRepository extends MongoRepository<Student, String> 

    Student findOneBySurname(String surname);
    Student findOneById(String id);
    Student findOneBySurnameAndFirstName(String surname, String firstName);

当我执行 studentRepository.findAll() 时,我能够获取数据库中的所有学生,并且所有字段都正确填充。

当我执行 studentRepository.findOne("528bb0e2e4b0442f1479f1b4") 或 studentRepository.findOneById)"528bb0e2e4b0442f1479f1b4") 时,它返回 null

当我对 mongo 查询进行调试时,我可以看到它正在调用:

2015-11-19 16:06:32.327 DEBUG 87081 --- [           main] o.s.data.mongodb.core.MongoTemplate      : findOne using query:  "id" : "528bb0e2e4b0442f1479f1b4" fields: null for class: class com.ad.josh.domain.Student in collection: Student
    2015-11-19 16:06:32.331 DEBUG 87081 --- [           main] o.s.data.mongodb.core.MongoDbUtils       : Getting Mongo Database name=[joshdb]
    2015-11-19 16:06:32.333 DEBUG 87081 --- [           main] o.s.data.mongodb.core.MongoTemplate      : findOne using query:  "_id" :  "$oid" : "528bb0e2e4b0442f1479f1b4" in db.collection: josh.Student

通常我可以看到它仍在使用对象 ID 而不是普通字符串进行查询。

我看到了以下建议:

Create a Converter which throws a RuntimeException

但这不起作用;它只是抛出一个 RuntimeException。

任何关于如何使用 Spring Data 访问 Id 已经定义为 String 的 Mongo 数据库(在现有数据库中)的建议将不胜感激。

【问题讨论】:

这个看起来很像 Spring Data MongoDB 中的一个错误,因为QueryMapper 将任何有效的String 转换为ObjectIdObjectId(请参阅:QueryMapper#convertId)。我创建了DATAMONGO-1333 来对此进行调查。 感谢@ChristophStrobl,这有助于了解 相当于expected and documented。作为有效 ObjectIds 的字符串在数据库中保持不变。是否愿意提供一个测试用例来显示存储库查询未返回刚刚持久化实例的实例? 所以@OliverGierke如果一个对象(Java)的id是一个字符串并且是持久化的,它会使用ObjectId保存到数据库中,如果你继续通过提供id来返回一个实例它返回的字符串很好。但是问题是存在一个现有数据库,其 id 未在 MongoDB 中存储为 ObjectId。如果继续使用 id (As String) 进行查询,它会返回 null,因为它期望 MongoDB 有使用 ObjectIds 存储的记录(虽然它存储在纯字符串中) 【参考方案1】:

一种可行的解决方法是为 ObjectId 创建您自己的转换器。

您可以通过提供 MappingMongoConverter 类型的 @Bean 来自定义 mongo 映射器

@Configuration
public class MongoConfig 

    @Bean
    public CustomConversions customConversions() 
        List<Converter<?, ?>> converters = new ArrayList<Converter<?, ?>>();
        converters.add(new MyObjectIdConverter());
        return new CustomConversions(converters);
    

    @Bean
    public MappingMongoConverter mappingMongoConverter(MongoDbFactory factory, MongoMappingContext context, BeanFactory beanFactory) 
        DbRefResolver dbRefResolver = new DefaultDbRefResolver(factory);
        MappingMongoConverter mappingConverter = new MappingMongoConverter(dbRefResolver, context);
        try 
            mappingConverter.setCustomConversions(customConversions());
        
        catch (NoSuchBeanDefinitionException ignore) 

        // Don't save _class to mongo
        mappingConverter.setTypeMapper(new DefaultMongoTypeMapper(null));

        return mappingConverter;
    


【讨论】:

代码不会做它声称的事情。此代码不允许将 String 用于 ObjectId 的 _id insetad,因此它不是正确答案。【参考方案2】:

我认为这里的问题是错误的“_id”格式,这就是为什么 Mongo 无法识别任何具有此类 id 的对象。它应该看起来像 ObjectId("528bb0e2e4b0442f1479f1b4"),而不是纯字符串类型。

        
            "_id" : ObjectId("528bb0e2e4b0442f1479f1b4"),
            "schoolId" : "URDLKYLFJXLWJGR193778316742298",
            "surname" : "Lewis",
            "admissionNumber" : "0347",
            "firstName" : "Martins"
        

因此,如果您已将带有现有数据的 .json 导入 mongo,则需要更改 id:

"_id": $oid:"528bb0e2e4b0442f1479f1b4",

而且应该转换成正确的格式。

希望它可以帮助某人。

【讨论】:

【参考方案3】:

您可以对每个存储库使用不同的MongoTemplate,如下所述:Configure Multiple MongoDB repositories with Spring Data Mongo 在您不希望从 String 转换为 ObjectId 的存储库中,您必须使用缺少 ObjectIdToStringConverter

的 mongoTemplate

【讨论】:

我也建议这样做。【参考方案4】:

有一个转换器可以为您进行开箱即用的转换,如果您想保持字符串 id 的原样而不将它们转换为 ObjectId,您必须覆盖 MappingMongoConverter 类中的 convertId 方法,我在这里做了一个例子:

/*
* Note that this example I've tried on spring-data-mongodb.3.0.4.RELEASE version 
* and it's not guaranteed that it will work with earlier versions 
* yet the approach should be similar
*/
@Component
public class CustomMappingMongoConverter extends MappingMongoConverter 

    public CustomMappingMongoConverter(MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext)     
        //The constructor can differ based on the version and the dbRefResolver instance as well
        super(NoOpDbRefResolver.INSTANCE, mappingContext);
    

    @Override
    public Object convertId(Object id, Class<?> targetType) 
        if (id == null) 
            return null;
         else if (ClassUtils.isAssignable(ObjectId.class, targetType) && id instanceof String && ObjectId.isValid(id.toString())) 
            return id;
        
        return super.convertId(id, targetType);
    


【讨论】:

以上是关于如何在没有 ObjectId 的情况下将 Mongo 与 Spring Data 一起使用的主要内容,如果未能解决你的问题,请参考以下文章

如何在没有 usermod 的情况下将用户添加到组?

如何在没有循环的情况下将文件逐行读入变量

如何在没有IB的情况下将2个按钮添加到右侧的UINavigationbar?

如何在没有副本的情况下将结构转换为字节数组?

如何在没有 foreach 的情况下将项目从列表复制到列表?

如何在没有外部 css 文件的情况下将 div 元素居中