EclipseLink ManyToOne - CriteriaBuilder 生成的查询是错误的
EclipseLink ManyToOne - CriteriaBuilder Generated Query is Wrong
我有一个实体与另一个实体的主键具有多对一关系。当我创建一个引用此外键的查询时,eclipseLink 总是创建一个连接而不是简单地访问外键。
我创建了一个高度简化的示例来说明我的问题:
@Entity
public class House {
@Id
@Column(name = "H_ID")
private long id;
@Column(name = "NAME")
private String name;
@ManyToOne
@JoinColumn(name = "G_ID")
private Garage garage;
}
@Entity
public class Garage{
@Id
@Column(name = "G_ID")
private long id;
@Column(name = "SPACE")
private Integer space;
}
我使用 CriteriaBuilder 创建了一个查询,该查询应该 return 所有没有车库或有车库且 G_ID = 0 的房屋。
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<House> query = cb.createQuery(House.class);
Root<House> houseRoot = query.from(House.class);
Path<Long> garageId = houseRoot.get(House_.garage).get(Garage_.id);
query.where(cb.or(cb.equal(garageId , 0), cb.isNull(garageId)));
TypedQuery<House> typedQuery = entityManager.createQuery(query);
List<House> houses = typedQuery.getResultList();
生成的查询是:
SELECT h.NAME, h.G_ID FROM HOUSE h, GARAGE g WHERE (((h.G_ID= 0) OR (g.G_ID IS NULL)) AND (g.G_ID = h.G_ID));
我不明白为什么
- or 条件首先引用 table HOUSE,然后引用 GARAGE(而不是 HOUSE)
- 首先创建连接。
根据我的理解,正确的查询应该是这样的:
SELECT h.NAME, h.G_ID FROM HOUSE h WHERE (((h.G_ID= 0) OR (h.G_ID IS NULL));
或者,如果进行连接,则应考虑到 ManyToOne 关系可以为空,因此执行 LEFT OUTER JOIN。
SELECT h.NAME, h.G_ID FROM HOUSE h LEFT OUTER JOIN GARAGE g ON (h.G_ID = g.G_ID ) WHERE (h.G_ID = 0) OR (g.G_ID IS NULL);
(请注意,这两个查询在我更复杂的设置中都能正常工作。当我只想检索所有没有车库的房屋时,我也会遇到同样的错误。)
我怎样才能做到这一点(同时仍然使用 CriteriaBuilder 并且理想情况下不必更改数据库模型)?
(请让我知道可能需要的任何其他信息,我对这个主题还很陌生,在迁移现有应用程序时遇到了这个问题。)
-- 编辑 --
我找到了解决我的问题的方法,该方法会导致行为略有不同(但在我的应用程序中,我必须迁移的那部分代码首先没有多大意义)。而不是使用
Path<Long> garageId = houseRoot.get(House_.garage).get(Garage_.id);
我用
Path<Garage> garage = houseRoot.get(House_.garage);
然后如预期的那样 table 不再加入车库。 (我假设之前的代码一定是某种 hack,以便从 openJPA 获得所需的行为)
I don't understand why
- The or condition first references table HOUSE and then GARAGE (instead of HOUSE)
我相信这是特定于实现的;无论如何,它不应该对结果有任何影响。
- The join is created in the first place.
通过说 Path<Long> garageId = houseRoot.get(House_.garage).get(Garage_.id)
,您基本上是在告诉 EclipseLink:“加入 Garage
到 House
,我们将需要它”。然后您访问 Garage_.id
(而不是 Garage_.space
)是无关紧要的。
如果您不想连接,只需将 G_ID
列再次映射为简单的 属性: @Column(name = "G_ID", insertable = false, updatable = false) private Long garageId
。然后在查询中参考 House_.garageId
。
Or if a join is made it should take into account that the ManyToOne relationship is nullable and therefore do a LEFT OUTER JOIN.
Path.get(...)
始终默认为 INNER JOIN
。如果您想要不同的联接类型,请使用 Root.join(..., JoinType.LEFT)
,即。 e. houseRoot.join(House_.garage, JoinType.LEFT).get(Garage_.id)
.
导致相同行为的一个解决方案是:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<House> query = cb.createQuery(House.class);
Root<House> houseRoot = query.from(House.class);
Path<Garage> garage = houseRoot.get(House_.garage);
Path<Long> garageId = garage.get(Garage_.id);
query.where(cb.or(cb.equal(garageId , 0), cb.isNull(garage)));
TypedQuery<House> typedQuery = entityManager.createQuery(query);
List<House> houses = typedQuery.getResultList();
结果如下 SQL:
SELECT H_ID, NAME, G_ID FROM HOUSE WHERE ((G_ID = 0) OR (G_ID IS NULL));
我有一个实体与另一个实体的主键具有多对一关系。当我创建一个引用此外键的查询时,eclipseLink 总是创建一个连接而不是简单地访问外键。
我创建了一个高度简化的示例来说明我的问题:
@Entity
public class House {
@Id
@Column(name = "H_ID")
private long id;
@Column(name = "NAME")
private String name;
@ManyToOne
@JoinColumn(name = "G_ID")
private Garage garage;
}
@Entity
public class Garage{
@Id
@Column(name = "G_ID")
private long id;
@Column(name = "SPACE")
private Integer space;
}
我使用 CriteriaBuilder 创建了一个查询,该查询应该 return 所有没有车库或有车库且 G_ID = 0 的房屋。
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<House> query = cb.createQuery(House.class);
Root<House> houseRoot = query.from(House.class);
Path<Long> garageId = houseRoot.get(House_.garage).get(Garage_.id);
query.where(cb.or(cb.equal(garageId , 0), cb.isNull(garageId)));
TypedQuery<House> typedQuery = entityManager.createQuery(query);
List<House> houses = typedQuery.getResultList();
生成的查询是:
SELECT h.NAME, h.G_ID FROM HOUSE h, GARAGE g WHERE (((h.G_ID= 0) OR (g.G_ID IS NULL)) AND (g.G_ID = h.G_ID));
我不明白为什么
- or 条件首先引用 table HOUSE,然后引用 GARAGE(而不是 HOUSE)
- 首先创建连接。
根据我的理解,正确的查询应该是这样的:
SELECT h.NAME, h.G_ID FROM HOUSE h WHERE (((h.G_ID= 0) OR (h.G_ID IS NULL));
或者,如果进行连接,则应考虑到 ManyToOne 关系可以为空,因此执行 LEFT OUTER JOIN。
SELECT h.NAME, h.G_ID FROM HOUSE h LEFT OUTER JOIN GARAGE g ON (h.G_ID = g.G_ID ) WHERE (h.G_ID = 0) OR (g.G_ID IS NULL);
(请注意,这两个查询在我更复杂的设置中都能正常工作。当我只想检索所有没有车库的房屋时,我也会遇到同样的错误。)
我怎样才能做到这一点(同时仍然使用 CriteriaBuilder 并且理想情况下不必更改数据库模型)?
(请让我知道可能需要的任何其他信息,我对这个主题还很陌生,在迁移现有应用程序时遇到了这个问题。)
-- 编辑 -- 我找到了解决我的问题的方法,该方法会导致行为略有不同(但在我的应用程序中,我必须迁移的那部分代码首先没有多大意义)。而不是使用
Path<Long> garageId = houseRoot.get(House_.garage).get(Garage_.id);
我用
Path<Garage> garage = houseRoot.get(House_.garage);
然后如预期的那样 table 不再加入车库。 (我假设之前的代码一定是某种 hack,以便从 openJPA 获得所需的行为)
I don't understand why
- The or condition first references table HOUSE and then GARAGE (instead of HOUSE)
我相信这是特定于实现的;无论如何,它不应该对结果有任何影响。
- The join is created in the first place.
通过说 Path<Long> garageId = houseRoot.get(House_.garage).get(Garage_.id)
,您基本上是在告诉 EclipseLink:“加入 Garage
到 House
,我们将需要它”。然后您访问 Garage_.id
(而不是 Garage_.space
)是无关紧要的。
如果您不想连接,只需将 G_ID
列再次映射为简单的 属性: @Column(name = "G_ID", insertable = false, updatable = false) private Long garageId
。然后在查询中参考 House_.garageId
。
Or if a join is made it should take into account that the ManyToOne relationship is nullable and therefore do a LEFT OUTER JOIN.
Path.get(...)
始终默认为 INNER JOIN
。如果您想要不同的联接类型,请使用 Root.join(..., JoinType.LEFT)
,即。 e. houseRoot.join(House_.garage, JoinType.LEFT).get(Garage_.id)
.
导致相同行为的一个解决方案是:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<House> query = cb.createQuery(House.class);
Root<House> houseRoot = query.from(House.class);
Path<Garage> garage = houseRoot.get(House_.garage);
Path<Long> garageId = garage.get(Garage_.id);
query.where(cb.or(cb.equal(garageId , 0), cb.isNull(garage)));
TypedQuery<House> typedQuery = entityManager.createQuery(query);
List<House> houses = typedQuery.getResultList();
结果如下 SQL:
SELECT H_ID, NAME, G_ID FROM HOUSE WHERE ((G_ID = 0) OR (G_ID IS NULL));