[英]How do I refer to columns only mapped for joins in JPQL?
Let's say I have the following two tables: 假设我有以下两个表:
@Entity public class Foo {
@Id private int id;
@ManyToOne()
@JoinColumn(name = "bar_id")
private Bar bar;
}
@Entity public class Bar {
@Id private int id;
private Boolean flag;
}
And I want to write a JPQL query that changes some Bar.flag
values based on a collection of Foo
ids. 我想编写一个JPQL查询,根据
Foo
ID的集合更改一些Bar.flag
值。
If this were plain SQL I'd write something like this: 如果这是普通的SQL我会写这样的东西:
UPDATE Bar SET flag = true WHERE id IN (SELECT bar_id from FOO where id = 3);
You can't translate this to JPQL however, because the bar_id
column isn't mapped to a property of the entity. 但是,您无法将其转换为JPQL,因为
bar_id
列未映射到实体的属性。
As bar_id
isn't directly mapped on the Foo
Entity, how can I achieve this kind of query in JPQL? 由于
bar_id
没有直接映射到Foo
实体, 我如何在JPQL中实现这种查询?
If you want a working bulk update query, the following should work: 如果您需要有效的批量更新查询,则以下内容应该有效:
UPDATE Bar b SET flag = true WHERE b IN (SELECT f.bar from FOO f where id = 3);
The idea is to work with entities, when you make the subselect. 当您进行子选择时,我们的想法是与实体一起工作。
One of the key features of JPA is abstracting away the nitty-gritty database-details (like foreign keys) from your Java code. JPA的一个关键特性是从Java代码中抽象出细节数据库细节(如外键)。 There are multiple ways of achieving the query you are outlining, here are a couple:
有多种方法可以实现您所概述的查询,以下是一对:
@Transactional
public void updateBarFlagForFoo(final int fooId, final boolean newFlagValue) {
final Foo foo = em.find(Foo.class, fooId);
foo.getBar().setFlag(newFlagValue);
}
or, in bulk: 或者,散装:
@Transactional
public void updateFlags(final List<Integer> fooIds, final boolean newFlagValue) {
final Query query = em.createQuery(
"update Bar b set flag = :newFlagValue where b.id in " +
"(select f.bar.id from Foo f where f.id in (:fooIds))");
query.setParameter("newFlagValue", newFlagValue);
query.setParameter("fooIds", fooIds);
query.executeUpdate();
}
Note that such an update will not change any entity retrieved within the same transaction. 请注意,此类更新不会更改在同一事务中检索的任何实体。 Doing
干
final Foo foo = em.find(Foo.class, 1);
updateFlags(Arrays.asList(1), true);
will not change the actual Foo-object. 不会改变实际的Foo对象。
My preferred way of doing this would be something like this: 我这样做的首选方式是这样的:
@Transactional
public void updateFlags(final List<Integer> fooIds, final boolean newFlagValue) {
final List<Bar> bars = findBarsForFooIds(fooIds);
for (final Bar bar : bars) {
bar.setFlag(newFlagValue);
}
}
private List<Bar> findBarsForFooIds(final List<Integer> fooIds) {
final TypedQuery<Bar> q =
em.createQuery("select distinct b from Foo f join f.bar b where f.id in (:fooIds)",
Bar.class);
q.setParameter("fooIds", fooIds);
return q.getResultList();
}
You could of course create a @Column
that maps the bar_id
, but I would not recommend this, as it kind of defeats the purpose of JPA
. 你当然可以创建一个映射
bar_id
的@Column
,但我不推荐这个,因为它有点违背了JPA
的目的。
You cannot access directly the bar_id
. 您无法直接访问
bar_id
。 You need to do an "extra" join between Foo
and Bar
to use the id
information from Bar
entity. 您需要在
Foo
和Bar
之间进行“额外”连接,以使用Bar
实体的id
信息。
So, your JPQL query will be like this: 所以,你的JPQL查询将是这样的:
UPDATE Bar bar SET bar.flag = true
WHERE bar.id IN
(SELECT barFoo.id from FOO foo
JOIN foo.bar barFoo
WHERE foo.id = 3);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.