简体   繁体   English

如何修复 JPA 规范 API 与条件连接返回 2 个查询而不是 1?

[英]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;我需要使用 JPA 作为查询;

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.子表与其父表的 OneToMany 关系在哪里。

Using JPA Spec API I have added below Predicate for Join使用 JPA Spec API 我在下面添加了连接谓词

 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并为 PARENT 添加了其他谓词,但我的查询作为 2 个不同的查询执行

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.在第二个查询中,这里获取所有带有 child.id 的记录(没有状态检查),因此也给我处于“已删除”状态的记录。

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 .要在单个查询中获取父实例及其子实例,您需要查看与您使用的“连接”语义不同的获取连接 - 请参阅https://docs.oracle.com/javaee/6/api/ javax/持久性/标准/FetchParent.html Calling root.fetch(PARENT_.ChildList, JoinType.LEFT) will cause the Parent and children to be fetched in the same SQL statement调用root.fetch(PARENT_.ChildList, JoinType.LEFT)将导致在同一个 SQL 语句中获取 Parent 和 children

When you look at fetch joins though, you'll see that they do not support query filtering.但是,当您查看 fetch join 时,您会发现它们不支持查询过滤。 When JPA returns managed entities, because it must manage changes to those instances, they must reflect exactly what is in the database.当 JPA 返回托管实体时,因为它必须管理对这些实例的更改,所以它们必须准确反映数据库中的内容。 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.您只是无法通过 JPA 语义按需获取子项的父项和部分列表。

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.这将为您提供一个 Object[} 列表,其中包含子项和父项对,但仅包含活动的子实例。 The Parent instance will all be the single instance with id equal to the value you pass in as a query parameter. Parent 实例将全部是 id 等于您作为查询参数传入的值的单个实例。 If you access its parent.ChildList though, you will find all its children - so don't.但是,如果您访问它的 parent.ChildList,您会找到它的所有子项——所以不要。 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:尝试使用 JPQL 数据查询:

@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这将返回一个 List<Object[]>,Object[] 包含每一行的数据,无需使用 Spring 规范

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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