J.Blake
J.Blake

Reputation: 39

Spring Mongo Repository is unable to match String to ObjectId in _id field

I have a data class with a String Id and I'm saving it into the mongoDB with a null value to get an autogenerated replacement.

@Document(collection="applications")
@TypeAlias(Constants.Application)
public class DataApplication implements IApplication{

@Id
@Field("id")
private String id = null;

...
}

This works as expected however I'm facing a problem in that I cannot seem to findById from the Spring Mongo Repo. It is capable of persisting and retrieving without problems, creating BSON like

{
   "_id" : ObjectId("5ef9a579bafa09b36225e743c"),
   Other fields and things
}

However, this seems to be specifically about when attempting to find using the id field. Its very weird that it isn't using the Id field name I specified, however as when I retrieve multiple objects at once they convert back into POJOs as expected I do not believe this is an issue.

From what I can see this seems to be a result of the MongoDB storing the values as ObjectId's, as I am able to retrieve by Id when I persist an object with a String Id value, when I pull the Applications into Java to compare, or when I create Examples that match all fields except Id, however Examples fails when I set an Id to compare against.

Prevent Spring Data for Mongo to convert ids to ObjectId seems to be related as it mentions the underlying query structure, unfortunately I seem to have the opposite problem.

Upvotes: 1

Views: 6780

Answers (2)

Achaari
Achaari

Reputation: 11

For details on how MongoDB handles _id fields and to address the issue of using ObjectId for new documents and String for updates, please refer to this: enter link description here

Upvotes: 0

prasad_
prasad_

Reputation: 14287

@Field annotation is used to define custom metadata for document fields. For example:

@Field(targetType = DECIMAL128)
private BigDecimal value;

@Field("fName")
private String firstName;

When you defined @Field("id") private String id = null; it is interpreted as the field id as another field id not the document identifier field _id. Because, the annotation's name attribute is specified as "id". You can verify this as well in the following example using the Person Pojo class:

public class Person {
    
    @Field("id")
    private String id = null;

    private String name;
    
    public Person(String name) {
        this.name = name;
    }

    public void setId(String id) {
        this.id = id;
    }

    // other get / set methods ...

    public String toString() {
       return "id: " + id + ", " + name;
    }
}

Using MongoRepository methods insert and query documents in person collection:

Person p1 = new Person("J.Doe");
//p1.setId("000");
repo.insert(p1);

List<Person> list = repo.findAll();
list.forEach(System.out::println);

This will insert a document (as queried from the mongo shell) and print the toString representation as:

{ "_id" : ObjectId("5ef5c92b70babc5a961350e5"), "name" : "J.Doe", "_class" : "com.example.demo.Person" }

Prints as: id: null, J.Doe

If you un-comment the code p1.setId("000"); and run it again, you will find the following:

{ "_id" : ObjectId("5ef5cab8306d071b13d3c16e"), "id" : "000", "name" : "J.Doe", "_class" : "com.example.demo.Person" }

And, prints as: id: 000, J.Doe

The code is behaving alright. Note the two fields, the _id and id. The @Field is to be used for customizing a document field.

To properly indicate the id field is to be used for _id document key field you can use the following. When the document is created the _id value will be the driver generated ObjectId:

  • Annotate the id field as @Id, @MongoId or the @Field (without the nameattribute).
  • Not annotate at all.

Note on Annotations:

  • @Id: Applied at the field level to mark the field used for identity purpose.
  • @MongoId: Applied at the field level to mark the field used for identity purpose. Accepts an optional FieldType to customize id conversion.
  • @Field: Applied at the field level it allows to describe the name and type of the field as it will be represented in the MongoDB BSON document thus allowing the name and type to be different than the field name of the class as well as the property type.

Upvotes: 2

Related Questions