简体   繁体   中英

How to fix JPA Specification API with join on condition returns 2 query instead of 1?

I have a requirement to form a query using JPA as;

select PARENT.ID, PARENT.NAME, CHILD.ID, CHILD.STATUS FROM PARENT

LEFT OUTER JOIN CHILD ON PARENT.ID=CHILD.ID AND CHILD.STATUS in ('Active')

WHERE PARENT.ID=?

Where child table is OneToMany relation to its parent.

Using JPA Spec API I have added below Predicate for Join

 public static Specification<PARENT> hasChildrenWithStatus(List<String> status) {
        return (root, criteriaQuery, criteriaBuilder)
                -> {
            Join<Object, Object> join = root.join(PARENT_.ChildList, JoinType.LEFT);
            join.on(join.get(CHILDREN_.STATUS).in(status));

            return criteriaBuilder.conjunction();
        };
    }

and added other predicates for PARENTs, but my query executed as 2 different queries

Hibernate: select PARENT.id as id1_0_, PARENT.name as namev2_0_ 
from PARENT PARENT 
left outer join CHILD CHILD on (PARENT.id=CHILD.id) and (CHILD.status in('Active'))
where PARENT.id=? and 1=1

Hibernate: select CHILD.id as id5_2_0_, CHILD.status as stat4_2_1_ from CHILD CHILD where CHILD.id=?

Here in the second query fetches all records with child.id(without status check), thus giving me records that are in "deleted" status as well.

How can I fix this? Also, Can this be executed as single query as my requirement?

You've got separate issues here. To fetch parent instances with its children in a single query, you need to look at fetch joins which are different from the 'join' semantics you've used - see https://docs.oracle.com/javaee/6/api/javax/persistence/criteria/FetchParent.html . Calling root.fetch(PARENT_.ChildList, JoinType.LEFT) will cause the Parent and children to be fetched in the same SQL statement

When you look at fetch joins though, you'll see that they do not support query filtering. When JPA returns managed entities, because it must manage changes to those instances, they must reflect exactly what is in the database. If you get a parent with only a partially populated list of children, it can't really know what to do when it saves the parent; should it modify that list or just ignore differences it finds from the database values. It is just not supported by the spec, and the query language doesn't allow it. You just can't fetch Parents and partial lists of the children on demand though JPA semantics.

What you can do though is fetch the children yourself. The equivalent of

"SELECT child, parent from Parent parent join parent.ChildList child where parent.id=:parentId and child.status in ('Active')"

This will give you a list of Object[} containing child and parent pairs, but only active child instances. The Parent instance will all be the single instance with id equal to the value you pass in as a query parameter. If you access its parent.ChildList though, you will find all its children - so don't. Mark it as lazy and don't touch it, and use the returned child instances in your operations.

Your query though is just asking for certain attributes from the tables, so I'm guessing you don't really need full Parent/Child instances. Try a JPQL data query instead:

@Query(value = "SELECT parent.id, parent.name, child.id, child.status from Parent parent join parent.ChildList child where parent.id=:parentId and child.status in ('Active')"

This will return a List<Object[]>, with the Object[] containing the data from each row, with no need to use Spring specifications

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM