Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fetchJoin(JoinFlag) cancelling #2645

Closed
devcken opened this issue Sep 17, 2020 · 8 comments
Closed

fetchJoin(JoinFlag) cancelling #2645

devcken opened this issue Sep 17, 2020 · 8 comments

Comments

@devcken
Copy link

devcken commented Sep 17, 2020

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!

@jwgmeligmeyling
Copy link
Member

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.

@devcken
Copy link
Author

devcken commented Sep 18, 2020

@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. 🙏

@jwgmeligmeyling
Copy link
Member

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()

@jwgmeligmeyling
Copy link
Member

Mind that you can also just remove all FETCH flags: query.getMetadata().getJoins().forEach(join -> join.getFlags().remove(FETCH))

@devcken
Copy link
Author

devcken commented Sep 18, 2020

@jwgmeligmeyling
Thank you for your help! In my case, your last help is best!

@jwgmeligmeyling
Copy link
Member

Good luck!

@msiniy
Copy link

msiniy commented Feb 20, 2021

Mind that you can also just remove all FETCH flags: query.getMetadata().getJoins().forEach(join -> join.getFlags().remove(FETCH))

But I believe getFlags method returns an unmodifiable Set, doesn't it? I'm also looking for a way to remove fetch flags from all joins.

@jwgmeligmeyling
Copy link
Member

Contrary to many other collections in QueryDsl, JoinExpression#getFlags actually returns the underlying, mutable, collection.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants