fetchJoin
is useful for traversing entity-graph. But, when projectioning is required, we could not use it.
When projectioning(e.g. min/max/count) is required, I wanna instantiate a original JPAQuery and then query fetchJoin-canceled JPAQuery. For example(in Kotlin):
val q = from(foo).innerJoin(bar).fetchJoin().where(...).orderBy(...)
val maxId = q.select(foo.id.max()).removeFlag(FETCH).fetchOne()
val result = q.where(foo.id.lt(maxId)).fetch()
DefaultQueryMetadata
has addJoinFlag(...)
but no removeJoinFlag(...)
. It would be great to have it!
The Query API follows a builder pattern in which join flags are only applied to the last join. There is no sensible way to use a removeFlag
, because it would have to be called right after the intended flag was added for reliable results.
The proper way to do this is to find the query root from the joins list, and remove the flag from the join:
i.e.: query.getMetadata().getJoins().stream().filter(join -> join.getTarget().equals(bar)).getFlags().remove(FETCH)
We could consider an API to more fluently retrieve the query root, i.e. query.getMetadata().getJoin(bar).getFlags().remove(FETCH)
Unfortunately, I have to reject the idea of the removeFlag
because it just won't work as intended.
@jwgmeligmeyling
First of all, thank you for your response 馃憤
I don't understand that it just won't work as intended.
I already implemented it using java reflection as follow(It works!):
val joinFlagsField: Field = metadata.javaClass.declaredFields
.first { it.name == "joinFlags" }
.also { it.isAccessible = true }
val joinFlags: Set<JoinFlag> = joinFlagsField.get(metadata) as Set<JoinFlag>
if (joinFlags.contains(FETCH)) {
joinFlagsField.set(metadata, CollectionUtils.removeSorted(joinFlags, FETCH))
}
I don't like that, of course. Because it is implemented using reflection as I said.
The way to find the query root from the joins list, yes it is a clear valid method, but not universal. I wanna make my ItemReader universal; As your method, ItemReader must know what is join path.
I hope you consider it one more time. 馃檹
Its not universal, because one might have several joins:
from(foo)
.innerJoin(bar).fetchJoin()
.innerJoin(baz).fetchJoin()
.innerJoin(buz)
.where(...).orderBy(...)
Calling removeFetchJoin
here would attempt to remove the fetch flag from buz
- which is not even present - and it will leave the fetch joins for bar
and baz
alone.
Your implementation will be much more reliable (and not require any reflection) by using getMetadata().getJoins()#getFlags()
Mind that you can also just remove all FETCH
flags: query.getMetadata().getJoins().forEach(join -> join.getFlags().remove(FETCH))
@jwgmeligmeyling
Thank you for your help! In my case, your last help is best!
Good luck!