[JAVA-4833] Using @BsonId in a @BsonCreator constructor has confusing behavior Created: 27/Dec/22  Updated: 12/Jan/23  Resolved: 12/Jan/23

Status: Closed
Project: Java Driver
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Question Priority: Major - P3
Reporter: Andrew Marshall Assignee: Ross Lawley
Resolution: Done Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Related
related to JAVA-4838 Support using @BsonId with @BsonCreator Closed
is related to JAVA-2599 Improve how the `ConventionAnnotation... Closed

 Description   

Java Driver Version: 4.7

Why is there a difference in behavior between using @BsonProperty("_id") and @BsonId in the constructor of a model annotated with @BsonCreator?

  • I saw issues when try to deserialize a document from the DB into the following model

I was getting the following error:

 

"Can't find a codec for class com.xgen.cloud.services.authz._private.model.aggregation.PermissionUsageCount"

 

Document from the DB

 

{
	_id: {
		service: String,
		type: String,
		action: String
	},
	numUsages: long
} 

Model's constructor

 

 

  @BsonCreator
  public PermissionUsageCount(
      @BsonId final Permission id,
      @BsonProperty("numUsages") final long numUsages) {
    this.id = id;
    this.numUsages = numUsages;
  } 

Notably, the model did not have any getters or settings on the ID field. Permission was defined as Model with the standard constructor using @BsonProperty annotations for each field in the document above

 

 

When I updated the constructor to the following I no longer saw any issues

 

  @BsonCreator
  public PermissionUsageCount(
      @BsonProperty("_id") final Permission id,
      @BsonProperty("numUsages") final long numUsages) {
    this.id = id;
    this.numUsages = numUsages;
  } 

In addition adding any of the following things while using @BsonId in the constructor also worked

 

  1. A getter on the corresponding field (following Java Bean naming conventions)
  2. A setter on the corresponding field (following Java Bean naming conventions)
  3. BsonProperty("_id")  annotation on the instance variable itself

Looking at the java driver's documentation is says you need to have all parameters in a @BsonCreator constructor, but it looks like that is not the case as well: https://www.mongodb.com/docs/drivers/java/sync/current/fundamentals/data-formats/pojo-customization/#annotations
So it looks like there is some gap in functionality between using @BsonProperty("_id") and @BsonId that adding any of the following three mentioned above.
 
The docs for @BsonId are pretty scarce only summarizing the annotation as doing the following: "Marks a property to serialize as the _id property." But it can be used as a way to deserialize the _id property from the DB as well. It might be good to have one of the following things added or clarified in the documentation * Can @BsonId be used in a @BsonCreator constructor to deserialize a document from the DB? If so in what conditions?

  • What functional difference is there between @BsonId and @BsonProperty("_id")

Also if there is a desire to have no functional difference between @BsonId and @BsonProperty("_id") it would be good to address the current gap in functionality



 Comments   
Comment by Andrew Marshall [ 12/Jan/23 ]

Hi Ross! Thanks for the detailed response! Great to hear that the issue was able to be updated and that you flagged the docs to be updated as well. Feel free to close this out and I can watch the ticket you linked here for any updates.

Comment by Ross Lawley [ 12/Jan/23 ]

Hi andrew.marshall@mongodb.com,

Apologies for jumping to conclusions in my initial response - I had forgotten the work done in JAVA-2599.

This turns out to be more of a subtle bug than I initially realized - I've updated JAVA-4838. Essentially @BsonId also marks a property as the id field and due to some (incorrect) assumptions in the AnnotationConventionImpl class it requires the Id field to already be registered in the ClassModel. Under certain scenarios this might not be the case and you correctly identified the work arounds.

Please follow JAVA-4838 for updates.

Looking at the java driver's documentation is says you need to have all parameters in a @BsonCreator constructor, but it looks like that is not the case as well: https://www.mongodb.com/docs/drivers/java/sync/current/fundamentals/data-formats/pojo-customization/#annotations

I says you need to have all parameters in the @BsonCreator annotated with @BsonProperty. Which is not quite correct they need to be annotated with either @BsonProperty or @BsonId - so I've added DOCSP-27400 to get that updated.

All the best,

Ross

Comment by Ross Lawley [ 12/Jan/23 ]

Hi andrew.marshall@mongodb.com,

the scenario of using with @BsonCreator wasn't thought of at the time of development

Apologies, looks like I was incorrect here, looks there may be a bug in the CreatorExecutable (the code that handles BsonCreator).

Investigating further, now trying to reproduce the error.

Ross

Comment by Ross Lawley [ 12/Jan/23 ]

Hi andrew.marshall@mongodb.com,

Thanks for the ticket - good questions and a good point.

@BsonId marks a property as the id. Iirc it was added for users who may not know the MongoDB uses _id as the id field.
@BsonProperty marks a POJO property with the equivalent name.
@BsonCreator needs a way to link method parameters to property names and reuses @BsonProperty as those values there aren't lost at compile time.

What functional difference is there between @BsonId and @BsonProperty("_id")

There should be none and functionally it should be the same, the scenario of using with @BsonCreator wasn't thought of at the time of development. I've added JAVA-4838 to get that done.

Feel free to reach out on slack for general java driver questions #java as we prefer internal questions there or public questions out in the community forums.

Ross

Generated at Thu Feb 08 09:03:05 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.