The challenge:
I am trying to batch fetch a collection of nested entities from my database. The resulting dataset ranges in the thousands of entities so my approach is to fetch entities in a paged fashion based on this post. The data is fetched from a web-based frontend and the pure un-paged query takes up to 10 seconds (unacceptable).
The problem:
The "parent" entities are fetched correctly, however the "child" entities are seemingly not fetched. In the resulting list of entities from TestRepository.getRankedTests(...), the list of "child" entities is not initialised and accessing them will result in a LazyInitializationException
. This points in the direction of an issue with my SqlResultMapping
, but I am not able to see the mistake. I've tried to inject errors into my SqlResultMapping
for the child, and it causes hibernate to complain at runtime, so it seems to try to map my config to the properties of the child entity, though the uninitialised collection of child entities dumbfound me.
Parent entity (Test.kt):
@NamedNativeQuery(
name = "Test.getRankedTests",
query = """
select *
from (
select
*,
DENSE_RANK() over (
order by "o.id"
) rank
from (
select
o.id as "o.id",
o.version as "o.version",
a.id as "a.id",
a.organisation_id as "a.organisation_id",
a.type as "a.type"
from organisation o
left join address a on o.id = a.organisation_id
order by o.organisation_number
) o_a_an_c
) o_a_an_c_r
where o_a_an_c_r.rank > :min and o_a_an_c_r.rank <= :max
""",
resultSetMapping = "TestMapping"
)
@SqlResultSetMapping(
name = "TestMapping",
entities = [
EntityResult(
entityClass = Test::class,
fields = [
FieldResult(name = "id", column = "o.id"),
FieldResult(name = "version", column = "o.version"),
]
),
EntityResult(
entityClass = TestChild::class,
fields = [
FieldResult(name = "id", column = "a.id"),
FieldResult(name = "organisation", column = "a.organisation_id"),
]
),
]
)
@Entity
@Table(name = "organisation")
class Test(
@Id
val id: Long,
val version: Long,
@OneToMany(mappedBy = "organisation", cascade = [CascadeType.ALL], orphanRemoval = true)
val addresses: List<TestChild>,
)
Child entity (TestChild.kt):
@Entity
@Table(name = "address")
@Suppress("LongParameterList")
class TestChild(
@Id
val id: Long,
@ManyToOne(fetch = FetchType.LAZY)
val organisation: Test,
)
Repository (TestRepository.kt):
@Repository
interface TestRepository : JpaRepository<Test, Long> {
fun getRankedTests(
min: Long,
max: Long
): List<Test>
}