Reputation: 101
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
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):
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