Micronaut Data JDBC Nested Entities
Asked Answered
I

1

7

I have been investigating using Micronaut Data JDBC as an augmentation for existing jdbi queries. Is it possible to do nested MappedEntity's and return the children?

Example is for a simple Warehouse Management System with a relation:

company
+- warehouse
   +- inventory

What I'm wanting to be able to do is query up from the inventory table and get the parent of warehouse and grandparent of company. It is a one-to-many relationship with a company able to have multiple warehouses and a warehouse able to have multiple pieces of inventory.

My Entities look like

The example project I've been playing with is at wms-api

@JdbcRepository
interface CompanyRepository : PageableRepository<Company, UUID>


@MappedEntity
data class Company(

   @field:Id
   @field:GeneratedValue
   var id: UUID? = null,

   @field:GeneratedValue
   var timeCreated: OffsetDateTime? = null,

   @field:GeneratedValue
   var timeUpdated: OffsetDateTime? = null,

   var name: String
)
@JdbcRepository
interface WarehouseRepository : GenericRepository<Warehouse, UUID> { // used GenericRepository to allow the mapping of Company

   @Join(value = "company")
   fun findById(id: UUID): Warehouse?

   @Join(value = "company")
   fun findByCompany(company: Company, pageable: Pageable): Slice<Warehouse>

   @Join(value = "company")
   fun findAllByCompany(company: Company, pageable: Pageable): Slice<Warehouse>

   @Join(value = "company")
   fun existsByLocationAndCompany(location: String, company: Company): Boolean

   fun save(warehouse: Warehouse): Warehouse

   fun update(warehouse: Warehouse): Warehouse
}

@MappedEntity
data class Warehouse(

   @field:Id @field:GeneratedValue
   var id: UUID? = null,

   @field:GeneratedValue
   var timeCreated: OffsetDateTime? = null,

   @field:GeneratedValue
   var timeUpdated: OffsetDateTime? = null,

   var location: String,

   @field:Relation(value = MANY_TO_ONE)
   var company: Company
)
@JdbcRepository
interface InventoryRepository : GenericRepository<Inventory, UUID> {

   fun save(inventory: Inventory): Inventory

   @Join(value = "warehouse")
   // FIXME need some way to tell repo to also pull company which is attached to warehouse
   fun findById(id: UUID): Inventory?

}

@MappedEntity
data class Inventory(

   @field:Id
   @field:GeneratedValue
   var id: UUID? = null,

   @field:GeneratedValue
   var timeCreated: OffsetDateTime? = null,

   @field:GeneratedValue
   var timeUpdated: OffsetDateTime? = null,

   var manufacturer: String,

   var barcode: String,

   var name: String,

   @field:Relation(value = MANY_TO_ONE)  // FIXME the problem is caused by this.
   var warehouse: Warehouse,
)

Relevant part of the stacktrace

Caused by: io.micronaut.core.reflect.exception.InstantiationException: Null argument specified for [company]. If this argument is allowed to be null annotate it with @Nullable
    at io.micronaut.core.beans.AbstractBeanIntrospection.instantiate(AbstractBeanIntrospection.java:121)
    at io.micronaut.core.beans.BeanIntrospection.instantiate(BeanIntrospection.java:81)
    at io.micronaut.data.runtime.mapper.sql.SqlResultEntityTypeMapper.readEntity(SqlResultEntityTypeMapper.java:345)

I would like to not have to make the Warehouse.company property optional if possible.

In the linked project there is a docker-compose.yml under support that can be used to fire up Postgres and then run the test suite, and the problem should pop up as a failure.

Thanks!

Iconoclast answered 20/4, 2021 at 18:5 Comment(0)
I
10

Figured it out. Had to change the InventoryRepository's findById method to look like

@JoinSpecifications(
   Join(value = "warehouse"),
   Join(value = "warehouse.company")
)
fun findById(id: UUID): Inventory?
Iconoclast answered 21/4, 2021 at 22:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.