Issue
I have a collection that stores different documents representing instances of classes inheriting from the same abstract parent class. For each class I have a mongorepository defined as MongoRepository<Volvo, String>
that was supposed to query all objects in the collecitons that belong to _class
com.repo.Volvo
.
However, it seems that the repository queries all documents, not only Volvos, and casts them to instances of Volvo.
How can I make it work with the _class definition, other than adding a filter by _class? Thanks!
Solution
One easy solution is to have a collection for each derived class. Annotating each model with that concrete collection make sure that spring only fetches the concrete car model.
Consider the following example:
abstract class Car(
@field:Id
var id: String? = null,
var model: String,
var wheels: Int,
)
And two concrete models:
@Document(collection = "volvo")
class Volvo(
id: String? = null,
model: String,
wheels: Int
) : Car(id, model, wheels)
@Document(collection = "vw")
class Vw(
id: String? = null,
model: String,
wheels: Int
) : Car(id, model, wheels)
By defining a repository for each manufacturer querying each will be easy
interface VolvoRepository : MongoRepository<Volvo, String>
interface VwRepository : MongoRepository<Vw, String>
Now consider the following basic application wich adds some data into the database and reads then via injected reporitories:
fun main(args: Array<String>) {
SpringApplication.run(Application::class.java, *args)
}
@SpringBootApplication
@EnableMongoRepositories
open class Application {
@Bean
open fun init(volvoRepository: VolvoRepository, vwRepository: VwRepository): CommandLineRunner = CommandLineRunner {
volvoRepository.save(Volvo(model = "XC90", wheels = 4))
vwRepository.save(Vw(model = "Golf", wheels = 4))
println(volvoRepository.findAll().map { it::class.simpleName + " - " + it.id + " - " + it.model })
println(vwRepository.findAll().map { it::class.simpleName + " - " + it.id + " - " + it.model })
}
}
Result of those println
calls are:
[Volvo - 62c5fb0798ab2c534dccaab0 - XC90]
[Vw - 62c5fb0898ab2c534dccaab1 - Golf]
Hope that this helps. If you would like to have this example code as working reporitory let me know.
Edit 1: Another approach with just one collection
Having all cars within the same collection and present a repository interface with dedicated manufacturer accessor methods.
Models (Volvo
, Vw
) are the same as above
@Document(collection = "cars")
abstract class Car(
@field:Id
var id: String? = null,
var model: String,
var wheels: Int,
)
interface CarRepository : MongoRepository<Car, String> {
@Query("{\"_class\": \"fqn.to.your.Volvo\"}")
fun findVolvos(): List<Volvo>
@Query("{\"_class\": \"fqn.to.your.Vw\"}")
fun findVws(): List<Vw>
}
Using this sample application
@SpringBootApplication
@EnableMongoRepositories
open class Application {
@Bean
open fun init(
carRepository: CarRepository
): CommandLineRunner = CommandLineRunner {
carRepository.save(Volvo(model = "XC90", wheels = 4))
carRepository.save(Vw(model = "Golf", wheels = 4))
println("Volvos")
println(carRepository.findVolvos().map { it::class.simpleName + " - " + it.id + " - " + it.model })
println("Vws")
println(carRepository.findVws().map { it::class.simpleName + " - " + it.id + " - " + it.model })
println("All cars")
println(carRepository.findAll().map { it::class.simpleName + " - " + it.id + " - " + it.model })
}
}
The following result is printed:
Volvos
[Volvo - 62c69a2c792f8b7c13f999a0 - XC90]
Vws
[Vw - 62c69a2d792f8b7c13f999a1 - Golf]
All cars
[Volvo - 62c69a2c792f8b7c13f999a0 - XC90, Vw - 62c69a2d792f8b7c13f999a1 - Golf]
Answered By - wartoshika
Answer Checked By - Candace Johnson (JavaFixing Volunteer)