Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

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

How to get all columns of a bean to use in a projection ? #2166

Closed
Maze-fr opened this issue Jul 12, 2017 · 5 comments
Closed

How to get all columns of a bean to use in a projection ? #2166

Maze-fr opened this issue Jul 12, 2017 · 5 comments
Labels

Comments

@Maze-fr
Copy link

Maze-fr commented Jul 12, 2017

Hello everybody,

Here is the context :
I have a stupid database constraint that forces me to use a projection to get data from tables using joins conditions that use only one column of composite PKs (so not working with JPA specifications of @OneToMany and @ManyToOne).
My DBA doesn't want to hear about sequence generated IDs... she is very "old school" about DB modelling... she is in love with composite PKs... :(

So... I want to get all the columns of my main bean, and the other data I need.
Yes, it's simple, and I managed to do it by iterating on every column of my bean, no problem.
Something like that :
Projections.bean(qMainBean, qMainBean.firstField, ..., qOtherBean, qStupidJoinBean)

But here is my problem :
Every time I want to add a new column in my @Entity bean, I need to go to modify my query to add that new column in my projection. First, that's annoying, and second, it's not good for maintaining the code.

By searching on StackOverflow and then on QueryDSL documentation, I saw a .all() method on Q generated classes to get all the columns of my bean (that's a great thing !), but I don't have that method generated on my Q classes (that's a bad thing !).

How can I get that method on my Q classes ? Am I missing something ?

My configuration :
Spring Platform Athens-SR5 as "parent" project, so QueryDSL 4.1.4 (and I use all libs in dependencies : core, jpa, collections, codegen, sql and apt)
And for generating Q classes, I use com.querydsl.apt.hibernate.HibernateAnnotationProcessor with jpa and apt libs as dependencies in my POM.

@Maze-fr
Copy link
Author

Maze-fr commented Jul 20, 2017

So... I made an dirty alternative way to have dynamic column list...
For those interested or very curious, here it is :

// Dynamically gathering fields from QMainBean
final List<Expression<?>> columns = Arrays.stream(QMainBean.class.getDeclaredFields())
                .filter(field -> !Modifier.isStatic(field.getModifiers()))
                .map(field -> {
                    try {
                        return (Expression<?>) field.get(qMainBean);
                    }
                    catch (final Exception e) {
                        log.trace("should never happen", e);
                        return null;
                    }
                })
                .collect(Collectors.toList());

// Adding transient fields
CollectionUtils.addAll(columns, new Expression[] { qOtherBean, qStupidJoinBean });

Using an array is required for trans-typing in CollectionUtils.addAll(). And yes, I could add one by one, but in my real case, I have 7 objects to add and I'm lazy.

Now you all have seen the inside of my "dirty all()" :-D (OK... I go back to listen to Steel Panther...)

@6harat
Copy link

6harat commented Aug 19, 2018

@timowest,
Could you please help out here as I am also facing a similar issue for v4.1.4.

To clarify the entire issue, this is what is happening:

This format works fine:

select.from(mainTable)
.leftJoin(mainTable.chairSet, chair)
.transform(GroupBy.groupBy(mainTable.id)
    .as(Projections.bean(MainTable.class, 
                mainTable.name, mainTable.material, 
                GroupBy.set(Projections.bean(Chair.class,
                       chair.id, chair.width, chair.height)).as(mainTable.chairSet))));

However, if a try changing it to:

select.from(mainTable)
.leftJoin(mainTable.chairList, chair)
.transform(GroupBy.groupBy(mainTable.id)
    .as(Projections.bean(MainTable.class, 
                mainTable.name, mainTable.material, 
                GroupBy.set(chair).as(mainTable.chairSet))));

Note Diff: GroupBy.set(chair)

I get an error which is basically the result set returned the column name of one of the field (say width) of chair as col_1_1_ whereas based on the select clause, as printed in the logs, it should rather have been WIDTH_1_0_.

Not able to share the stack trace as it contains domain specific information related to internal infrastructure as we use custom drivers for connecting to the database.

This is why I am forced to use the former syntax which even though works fine but is way too verbose.

@isenlover
Copy link

same problem
all() method in documents,but my Q generator not exist

@jwgmeligmeyling
Copy link
Member

Why project all columns at all if you can just project the entity itself which contains the columns? This makes no sense to me in a JPA environment.

@LukeChow
Copy link

Why project all columns at all if you can just project the entity itself which contains the columns? This makes no sense to me in a JPA environment.

It makes sense when you select all columns of an entity(A) and join another entity(B) and select one column of B as a Transient variable in A.

class A {
long id,
string a1,
...
string a10,
@Transient
boolean statusInB;
}

class B {
long id,
long idOfA,
boolean status,
...
}

Now, this is my codes:

List<Tuple> result = queryFactory
                .select(qA, qB.status)
                .from(qA)
                .leftJoin(qB).on(qA.id.eq(qB.idOfA))
                .groupBy(qA.id)
                .fetch();

List<A> list = new ArrayList<>(result.size());
        for (Tuple tuple : result) {
            A a = tuple.get(0, A.class);
            Boolean status = tuple.get(1, Boolean.class);
            if (a != null && status != null) {
                a.setStatusInB(status);
            }
            list.add(a);
        }
return list;

But I want to do it with some codes like this:

List<A> result = queryFactory
                .select(Projections.bean(A.class, qA.all(), qB.status.as("statusInB")))
                .from(qA)
                .leftJoin(qB).on(qA.id.eq(qB.idOfA))
                .groupBy(qA.id)
                .fetch();
return result;

@querydsl querydsl locked and limited conversation to collaborators Jun 23, 2021

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
Projects
None yet
Development

No branches or pull requests

5 participants