【发布时间】:2018-04-13 18:39:00
【问题描述】:
我一直在编写代码来构建条件查询以进行条件连接。但它并不正确。请考虑以下示例:
我有 2 个实体 Owner 和 Car。 Owner 与 Car 有 OneToMany 关系。
代码Owner 实体有List 的Cars。(使用@OneToMany 和FetchType.LAZY)
所有者.java:
@Entity
public class Owner {
@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE)
private int id;
private String name;
private String email;
@OneToMany(mappedBy="owner",fetch=FetchType.LAZY)
private List<Car> cars;
...
}
汽车.java
@Entity
public class Car{
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private int id;
private String manufacturer;
private String regnumber;
@ManyToOne
@JoinColumn(name="ownerid")
private Owner owner;
...
}
现在我的要求很简单,我想获得所有 TESLA 汽车的车主。 以下代码适用于此:
CriteriaQuery<Owner> cq = cb.createQuery(Owner.class);
Root<Owner> rootowner = cq.from(Owner.class);
rootowner.fetch("cars");
cq.distinct(true);
Join<Owner, Car> carjoin= rootowner.join(Owner_.cars);
Expression<String> carmanExp = carjoin.get(Car_.manufacturer);
Predicate p = cb.like(carmanExp, "TESLA");
cq.where(p);
TypedQuery<Owner> tq = em.createQuery(cq);
现在我的要求是,如果Owner 拥有两辆汽车,一辆是特斯拉,另一辆是沃尔沃。我希望Owner 实体应该只包含TESLA。
我该怎么做?
【问题讨论】:
-
您正在选择
Owner对象,如果Owner对象有这 2 辆汽车,那么当加载该对象时,它将有这 2 辆汽车。您将不会返回半个Owner对象。曾经 -
但是当你运行一个普通的 SQL JOIN 查询
SELECT * FROM owner as o JOIN car as c on c.ownerid=o.id WHERE c.manufacturer='TESLA';它只返回一个元组。如果我们可以使用普通的旧 SQL 而不是 JPA,那么 JPA 对 SQL 或 JDBC 有什么优势? -
如果
OwnerJOHN 有 2 辆汽车,则返回为OwnerJOHN 的对象将始终有 2 辆汽车,因为 JOHN 有 2 辆汽车!这就是 O-O 的工作原理。如果您想执行平面 SQL 查询并将它们操作到仅包含部分信息的对象中,那就去吧,但是 JPA 是为 O-O 设计的,对象具有身份和关系。 -
但是如果我使用 JPQL 作为
TypedQuery<Owner> tq = em.createQuery("SELECT o FROM Owner o JOIN FETCH o.cars c WHERE c.manufacturer='TESLA'",Owner.class);运行相同的查询,这将给我Owner只有一辆车的对象。 O-O 在这里效果很好。我所需要的只是CriteriaAPI 中这个 JPQL 的等价物。我知道可以这样做,因为我听说有人已经这样做了,但我现在无法询问。 -
您的“正常”SQL 查询仍会为所有者返回多个结果 - 每辆特斯拉对应一行。但请注意,您的 SQL 查询关注的是汽车,而不是车主。在您的 JPA 示例中,您将获得完整的 Owner 实例并忽略汽车。您必须记住,在 JPA 中,查询条件仅用于选择返回的结果对象,而不是它们的构建方式。构建对象是基于您的映射,而不是您的查询,因此始终需要 owner.getCars 关系来根据映射反映数据库中的实际内容。
标签: java jpa eclipselink criteria criteriaquery