假设我有以下两个表:
@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查询,根据Bar.flag
ID的集合更改一些Foo
值。
如果这是普通的SQL我会写这样的东西:
UPDATE Bar SET flag = true WHERE id IN (SELECT bar_id from FOO where id = 3);
但是,您无法将其转换为JPQL,因为bar_id
列未映射到实体的属性。
由于bar_id
未直接映射到Foo
实体,如何在JPQL中实现此类查询?
答案 0 :(得分:2)
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();
}
你当然可以创建一个映射@Column
的{{1}},但我不建议这样做,因为这会使bar_id
的目的失效。