如何引用仅映射到JPQL中的连接的列?

时间:2015-11-19 17:33:37

标签: java jpa 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查询,根据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中实现此类查询?

1 个答案:

答案 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的目的失效。