Thibaud Sowa
Thibaud Sowa

Reputation: 412

QueryDsl: null entity from oneToMany association with projection bean

So I wanted to use projection bean in my project to optimize loading time (to avoid to get unusefull data...).

I have these two enties: Parent -----< Child (with one-to-many association)

In my database I have:

I wanted to get parent and children in one request so first I did this:

final JPAQueryBase<?> query = createQuery();

final QParent qParent = QParent.parent;
final QChild qChild = QChild.child;

Map<Long, Parent> transform = query.from(qParent)
        .leftJoin(qParent.children, qChild)
        .transform(GroupBy.groupBy(qParent.id)
                .as(Projections.bean(Parent.class,
                        qParent.id,
                        qParent.name,
                        GroupBy.set(qChild).as(qParent.children))));

final List<Parent> parents = new ArrayList<Parent>(transform.values());

It worked like a charm, the result is something like this:

[
    Parent: {
        id: 1,
        name: "parent1",
        children: [
            Children: {
                id: 1,
                name: "child1",
                otherAttr: //loaded
            },
            Children: {
                id: 2,
                name: "child2",
                otherAttr: //loaded
            }
        ],
        otherAttr: null
    },
    Parent: {
        id: 2,
        name: "parent2",
        children: [], //size: 0
        otherAttr: null
    }
]

But I didn't want to load the "other attributes" from child entity. So I did this query with a projection bean of Child entity:

final JPAQueryBase<?> query = createQuery();

final QParent qParent = QParent.parent;
final QChild qChild = QChild.child;

Map<Long, Parent> transform = query.from(qParent)
        .leftJoin(qParent.children, qChild)
        .transform(GroupBy.groupBy(qParent.id)
                .as(Projections.bean(Parent.class,
                        qParent.id,
                        qParent.name,
                        GroupBy.set(Projections.bean(Child.class,
                                qChild.id,
                                qChild.name))
                                .as(qParent.children))));

final List<Parent> parents = new ArrayList<Parent>(transform.values());

With this query, I have full control on the attributes to select. But when parent has no children, something strange happened: a Child object with all attributes set to null is present in children list like this:

[
    Parent: {
        id: 1,
        name: "parent1",
        children: [
            Children: {
                id: 1,
                name: "child1",
                otherAttr: null
            },
            Children: {
                id: 2,
                name: "child2",
                otherAttr: null
            }
        ],
        otherAttr: null
    },
    Parent: {
        id: 2,
        name: "parent2",
        children: [ //size: 1
            Children: {
                    id: null,
                    name: null,
                    otherAttr: null
                }
        ],
        otherAttr: null
    }
]

Is it a bug? If not what did I do wrong??

Upvotes: 1

Views: 3167

Answers (1)

Adrian Cox
Adrian Cox

Reputation: 6334

Having run into this myself, I'll place the answer from GitHub issue 1677 here for future reference. Individual projections must be marked with skipNulls():

Map<Long, Parent> transform = query.from(qParent)
    .leftJoin(qParent.children, qChild)
    .transform(GroupBy.groupBy(qParent.id)
            .as(Projections.bean(Parent.class,
                    qParent.id,
                    qParent.name,
                    GroupBy.list(Projections.bean(Child.class,
                            qChild.id,
                            qChild.name,
                            Projections.bean(qSubChild.class,
                                    qSubChild.id,
                                    qSubChild.name).skipNulls()
                                    .as(qChild.subChild)).skipNulls())
                            .as(qParent.children))));

Upvotes: 3

Related Questions