miberg81
miberg81

Reputation: 101

Can't deserialize a mongo db collection in a polymorphic way in Spring Boot

I have a mongo collection called "items" which contains jewelry. In each document of the collection I have a discriminator property "category" - which I'm using to deserialize the documents into separate classes like Pendant, Ring etc. using the jackson polymorphic annotations. I'm fetching items using an abstract class and a few implementations. I'm getting an error saying: "Failed to instantiate ......Jewelry using constructor protected ....Jewelry" Jewelery.java is my parent abstract class, which means the framework tries to use an abstract class instead of using my implementations. It seems the jackson "@JsonSubTypes" annotation is completely ignored, and I don't understand why[all this setup used to work 100% ok, but recently I discovered it doesn't any more]

Here is the example of the abstract class, following by one of the 5 implementations.

/***************Jewelery.java****************************/
@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.EXISTING_PROPERTY,
        property = "category",
        visible = true
)
@JsonSubTypes({
        @JsonSubTypes.Type(value = Bracelet.class, name = "BRACELETS"),
        @JsonSubTypes.Type(value = Earring.class, name = "EARRINGS"),
        @JsonSubTypes.Type(value = Pendant.class, name = "PENDANTS"),
        @JsonSubTypes.Type(value = Ring.class, name = "RINGS"),
        @JsonSubTypes.Type(value = Set.class, name = "SETS")
})
@Document(collection = "items")
@Getter
@Setter
public abstract class Jewelery implements Sellable {

    @Id
    private  String _id;
    private  String catId;`your text`
    private  String name;

    private Category category; // this is the discriminator property!

    private Integer importance;
    private  Integer price;
    private  Integer inStockQuantity;

    private  Boolean isDraft;
    private  Boolean isOnSale;

    private  Integer rating;
    private  Integer priority; 

    protected  String[] images; // single item images

    //single item properties;
    private  String[] materials;
    private Size size;

    private String _class;

    protected Jewelery(String _id, String catId, String name, Category category, Integer importance,Integer price, Integer inStockQuantity, Boolean isDraft, Boolean isOnSale, Integer rating, Integer priority, String[] images, String[] materials, Size size, String _class) {
        this._id = _id;
        this.catId = catId;
        this.name = name;
        this.category = category;
        this.importance = importance;
        this.price = price;
        this.inStockQuantity = inStockQuantity;
        this.isDraft = isDraft;
        this.isOnSale = isOnSale;
        this.rating = rating;
        this.priority = priority;
        this.images = images;
        this.materials = materials;
        this.size = size;
        this._class = _class;
    }
}
/*************Bracelet.java****************/
@TypeAlias("Bracelet")
public class Bracelet extends Jewelery implements Sellable {

    public Bracelet(String _id, String catId, String name, Category category, Integer importance, Integer price, Integer inStockQuantity, Boolean isDraft, Boolean isOnSale, Integer rating, Integer priority, String[] images, String[] materials, Size size, String _class) {
        super(_id, catId, name, category, importance, price, inStockQuantity, isDraft, isOnSale, rating, priority, images, materials, size, _class);
    }

    @Override
    public String[] getImages() {
        return images;
    }

    @Override
    public void setImages(String[] images) {
        this.images  = images;
    }
}
/*************my repository class**********************************/
@Repository("items")
public interface JeweleryRepository extends MongoRepository<Jewelery, String> {
}
/*****my service call***********************************/
public List<Jewelery> getItems(String sortBy) {
  return jewelryRepo.findAll();
}  

I tried removing @Document(collection = "items") from the parent abstract class and putting this annotation on all the implementation. It didn't work because mongo wouldn't recognize collection name and by default would try to bring items from non-existing "jewelry" collection, resulting in empty array response.

Upvotes: 2

Views: 173

Answers (1)

miberg81
miberg81

Reputation: 101

I found what caused this glitch: I accidentally deleted 5 MongoRepository interfaces which were based on concrete classes (rather than the abstarct Jewelery class): interfaces i deleted by mistake

I deleted those interfaces because intellij marked them as unused and indeed they are unused by my own code. In my own code i only do:

List<Jewelery> products =  jewelryRepo.findAll()

which uses the repository based on abstract class:

private final MongoRepository<Jewelery, String> jewelryRepo

But the fact is that those 5 more concrete interfaces are somehow used behind the scenes.

They show as unused, but the minute you delete them, you get back that annoying: "Failed to instantiate ......Jewelry using constructor ....Jewelry"

So, the conclusion is: For this whole abstract classes setup to work, it's not enough to have an abstarct Jewelery class and 5 concrete implementations (properly marked with Jackson annotation) - it's also needed to extend the MongoRepository based on abstarct class 5 times (each time with another concrete class - like i did with Pedant class on the picture)

If someone with deeper understanding could explain in detail what is going behind the scenes - i'd be grateful. I really hope it doesn't make 5 calls to db behind the scenes instead of one(althogh i don't see signs of it)

Upvotes: 0

Related Questions