如何在 JPQL 中引用仅为连接映射的列?
How do I refer to columns only mapped for joins in JPQL?
假设我有以下两个表:
@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;
}
我想编写一个 JPQL 查询,它根据 Foo
个 ID 的集合更改一些 Bar.flag
值。
如果这是简单的 SQL 我会这样写:
UPDATE Bar SET flag = true WHERE id IN (SELECT bar_id from FOO where id = 3);
但是您不能将其转换为 JPQL,因为 bar_id
列未映射到实体的 属性。
由于 bar_id
没有直接映射到 Foo
实体,如何在 JPQL 中实现这种查询?
您无法直接访问 bar_id
。您需要在 Foo
和 Bar
之间进行 "extra" 连接,以使用来自 Bar
实体的 id
信息。
因此,您的 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);
JPA 的一个关键特性是从您的 Java 代码中抽象出具体的数据库细节(如外键)。有多种方法可以实现您概述的查询,这里有几个:
@Transactional
public void updateBarFlagForFoo(final int fooId, final boolean newFlagValue) {
final Foo foo = em.find(Foo.class, fooId);
foo.getBar().setFlag(newFlagValue);
}
或者,批量:
@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();
}
请注意,此类更新不会更改同一事务中检索到的任何实体。正在做
final Foo foo = em.find(Foo.class, 1);
updateFlags(Arrays.asList(1), true);
不会改变实际的 Foo 对象。
我的首选方式是这样的:
@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();
}
你当然可以创建一个映射 bar_id
的 @Column
,但我不推荐这样做,因为它有点违背 JPA
.[=17= 的目的]
如果您想要一个有效的批量更新查询,以下应该有效:
UPDATE Bar b SET flag = true WHERE b IN (SELECT f.bar from FOO f where id = 3);
想法是在进行子选择时使用实体。
假设我有以下两个表:
@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;
}
我想编写一个 JPQL 查询,它根据 Foo
个 ID 的集合更改一些 Bar.flag
值。
如果这是简单的 SQL 我会这样写:
UPDATE Bar SET flag = true WHERE id IN (SELECT bar_id from FOO where id = 3);
但是您不能将其转换为 JPQL,因为 bar_id
列未映射到实体的 属性。
由于 bar_id
没有直接映射到 Foo
实体,如何在 JPQL 中实现这种查询?
您无法直接访问 bar_id
。您需要在 Foo
和 Bar
之间进行 "extra" 连接,以使用来自 Bar
实体的 id
信息。
因此,您的 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);
JPA 的一个关键特性是从您的 Java 代码中抽象出具体的数据库细节(如外键)。有多种方法可以实现您概述的查询,这里有几个:
@Transactional
public void updateBarFlagForFoo(final int fooId, final boolean newFlagValue) {
final Foo foo = em.find(Foo.class, fooId);
foo.getBar().setFlag(newFlagValue);
}
或者,批量:
@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();
}
请注意,此类更新不会更改同一事务中检索到的任何实体。正在做
final Foo foo = em.find(Foo.class, 1);
updateFlags(Arrays.asList(1), true);
不会改变实际的 Foo 对象。
我的首选方式是这样的:
@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();
}
你当然可以创建一个映射 bar_id
的 @Column
,但我不推荐这样做,因为它有点违背 JPA
.[=17= 的目的]
如果您想要一个有效的批量更新查询,以下应该有效:
UPDATE Bar b SET flag = true WHERE b IN (SELECT f.bar from FOO f where id = 3);
想法是在进行子选择时使用实体。