MongoDB Java Driver for Polymorphism

When using MongoDB with a strong OOP language like Java, it’s a no brainer to want to hack the MongoDB Java Driver to serialize data to different sub-classes of model classes given different shapes of data, because MongoDB doesn’t have a strict schema restriction for a collection, which makes it a perfect match to store data models with polymorphism or inheritance into the same collection. This post will explore into how can we adapt polymorphism to MongoDB Java Driver.

Example

The post will use invoice line items as an example to demonstrate how to achieve polymorphism in MongoDB.

For line item, it always comes with a SKU (Stock Keeping Unit) representing the type of products. When we store line items into a collection, line items with different SKUs might have different shapes. For different SKUs, we might want to store different extra information about the product. For example, if it’s a T-shirt, we’d like to include the size info for the specific item, and we also want to include the expiration date if the item is food. To represent them in Java code, we can either use inheritance or composition. We can achieve both with MongoDB Java Driver, but before we jumping into those 2 directions, I’ll first introduce some common setups for both directions.

Setup

We first need to have a MongoCollection object to access the collection.

Main.java

Note that we need to provide a CodecRegistry object, the getCodecRegistry will look like the following:

Main.java

The code snippet does the following things:

  1. Set the conventions to use DEFAULT_CONVENTIONS . The DEFAULT_CONVENTIONS has 3 different conventions — we will need 2 of them here: CLASS_AND_PROPERTY_CONVENTION and ANNOTATION_CONVENTION . More documentation for each convention can be found on their respective documentation page: CLASS_AND_PROPERTY_CONVENTION and ANNOTATION_CONVENTION
  2. Register other classes that could be used while encoding a LineItem object. It has different implication for inheritance and composition style, so getClassesToRegister will be elaborated for both of them respectively later.

Polymorphism from Inheritance

For inheritance style, the concrete class will contain detailed info for different types respectively. I’ll make the assumption that all different line items will have their special info, thus the following code:

LineItem.java

A few things to note here:

  1. As the base class for line item, @BsonDiscriminator(key = “sku”) needs to be set to let MongoDB Java Driver to know that the sku field will be used to differentiate which sub-class to use.
  2. All the fields needs to be saved in the database should be annotated with BsonProperty , and the id field needs to be annotated with BsonId . We also need to define the setters and getters for those fields.
  3. An empty constructor needs to be provided;
  4. Note that we can’t use BaseLineItem directly for encoding even it’s not abstract . The reason is that the driver can’t use a class with generic for codec. more detail, see the official doc. This actually makes sense, because for a class with generic, it’s easy to insert it into database, but it will have trouble loading the data from the database because the driver doesn’t know which class to use. To address this, we can make sub-class of BaseLineItem without generic, which the driver will happily accept.

MilkLineItem.java

Things to note:

  1. @BsonDiscriminator(key = “sku”, value = “MILK”) is provided here, letting the driver know that only when the sku equals MILK this sub-class will be used.
  2. An empty constructor is also needed here.

MilkInfo.java

We mentioned earlier that we will need to register other classes needs to be used by the driver. Here, we need to register MilkLineItem because the driver needs to know what classes to use when the discriminator key — sku, has its value equals MILK .

we will add the following:

Main.java

We can test the code by adding the following lines to the main method:

Main.java

BaseLineItem first = document.find().first();
System.out.println("line item quantity: " + first.getQuantity());

In mongoDB, we will see the following document:

Polymorphism from Composition

With composition style, we also assume that all the line items will have their specialized info. And since we use composition, we will use LineItem class directly to represent all the variations.

Note:

  1. We still have to note 2 and 3 listed for BaseLineItem in previous section
  2. Instead of @BsonDiscriminator(key=”sku”) , we simply put @BsonDiscriminator , which means that we will store a special field _t in the database, and the value will the full class name with it’s package path.

Info.java

MilkInfo.java

With composition style, as Info sub-classes are the ones holding BsonDiscriminator , the concrete Info classes needs to be registered, so we need to add the following to the main class:

Main.java

Again we can add the following code to the main method to test it works:

Main.java

In the database, the document looks a bit different from that when using inheritance:

Conclusion

That’s all you have to know when using MongoDB with Java to support Polymorphism. I’ll update later with a repo with sample code.

sharing whatever i learned in a hard way

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store