简体   繁体   English

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

[英]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. 您需要在FooBar之间进行“额外”连接,以使用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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM