Hibernate native SQL query - how to get distinct root entities with eagerly initialized one-to-many association
Asked Answered
L

1

1

I have two entities Dept and Emp (real case changed and minimized). There is 1:n association between them, i.e. properties Dept.empList and Emp.dept exist with respective annotations. I want to get List<Dept> whose elements are distinct and have collection empList eagerly initialized, using native SQL query.

session.createSQLQuery("select {d.*}, {e.*} from dept d join emp e on d.id = e.dept_id")
        .addEntity("d", Dept.class)
        .addJoin("e", "d.empList")
        //.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
        .list();

This query returns List<Object[2]> with instances of Dept, Emp (in that order) in the array and field Dept.empList properly initialized. That's ok.

To get distinct Depts, I thought setting transformer DISTINCT_ROOT_ENTITY (uncommenting the line) would be enough. Unfortunately, the DistinctRootEntityResultTransformer is based on RootEntityResultTransformer which treats the last element in tuple as root entity (it's hardwired). Since the order of entities is determined by sequence of addEntity, addJoin calls, the transformer mistakenly treats Emp as root entity and returns list with all Emps of all Depts.

Is there any clean way how to make Hibernate recognized the Dept as root entity, even though it is not last in entity list?

Note 1: I tried to switch order to .addJoin("e", "d.empList").addEntity("d", Dept.class). Does not work since d.empList requires d defined. Fails on HibernateSystemException : Could not determine fetch owner somewhere in Hibernate internals (org.hibernate.loader.Loader).

Note 2: I tried to define order as .addEntity("e", Emp.class).addJoin("d", "e.dept"). This seemingly works but the association is actually filled only from the "many" side. Hence the collection Dept.empList is some uninitialized proxy until requested, which invokes explicit SQL query and thus does not utilize the join in my query.

Note 3: The custom transformer looking for hard-wired index works:

        .setResultTransformer(new BasicTransformerAdapter() {
            public Object transformTuple(Object[] tuple, String[] aliases) {
                return tuple[0];
            }
            public List transformList(List list) {
                return DistinctResultTransformer.INSTANCE.transformList( list );
            }
        })

though I'm hesitant to accept such easy task could have such complicated solution.

Hibernate version: 3.6.10 (I know - legacy project :-) though I looked into source code of latest version and seems the key points don't differ).

Lookthrough answered 24/6, 2020 at 11:42 Comment(0)
L
0

Finally found https://mcmap.net/q/544097/-hibernate-native-sql-query-retrieving-entities-and-collections - make duplicate call of .addEntity to force root entity on the end of list:

    .addEntity("d", Dept.class)
    .addJoin("e", "d.empList")
    .addEntity("d", Dept.class)

It's still workaround but cleaner than mine and - based on 36 upvotes - it seems as idiomatic solution.

Lookthrough answered 24/6, 2020 at 13:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.