2015-11-19 9 views
9

Powiedzmy mam następujące dwie tabele:Jak mam się odwoływać do kolumn odwzorowanych tylko na połączenia w 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; 
} 

I chcę napisać kwerendę JPQL który zmienia niektóre wartości Bar.flag w oparciu o zbiór Foo identyfikatorów.

Gdyby to był zwykły SQL Chciałbym napisać coś takiego:

UPDATE Bar SET flag = true WHERE id IN (SELECT bar_id from FOO where id = 3); 

Nie można tłumaczyć to JPQL jednak, ponieważ kolumna bar_id nie są odwzorowywane na własność podmiotu.

Jako, że bar_id nie jest bezpośrednio mapowany na obiekcie Foo, , w jaki sposób mogę uzyskać tego rodzaju zapytanie w JPQL?

+0

gdzie jest kolekcja? –

+0

czy to nie odpowiada? http://stackoverflow.com/questions/3629259/how-do-i-do-a-jpql-subquery –

+0

@sashok_bg Nie próbowałem tego, ale nie sądzę, że tak, ponieważ nie mam surowe klucze obce odwzorowane w encji ('p.country_id' w ich przykładzie) – SCdF

Odpowiedz

3

Jeśli chcesz roboczej zapytanie o aktualizację zbiorczą dodaje powinno działać:

UPDATE Bar b SET flag = true WHERE b IN (SELECT f.bar from FOO f where id = 3);

Chodzi o to, aby pracować z innymi podmiotami, podczas dokonywania podselekcji.

0

Nie można uzyskać dostępu bezpośrednio do bar_id. Musisz wykonać dodatkowe połączenie między Foo i Bar, aby użyć informacji z jednostki id z jednostki Bar.

Więc zapytanie JPQL będzie tak:

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); 
2

Jedną z kluczowych cech JPA jest abstrahowanie precz bazy szczegóły sedna (jak kluczy obcych) z kodu Java. Istnieje wiele sposobów osiągania zapytanie jesteś określający, oto kilka:

@Transactional 
public void updateBarFlagForFoo(final int fooId, final boolean newFlagValue) { 
    final Foo foo = em.find(Foo.class, fooId); 
    foo.getBar().setFlag(newFlagValue); 
} 

lub luzem:

@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(); 
} 

Należy zauważyć, że taka zmiana nie zmieni każdy podmiot pobrane w ciągu taka sama transakcja. Wykonanie

final Foo foo = em.find(Foo.class, 1); 
updateFlags(Arrays.asList(1), true); 

nie zmieni faktycznego obiektu Foo.

Mój preferowanym sposobem osiągnięcia tego celu byłoby coś takiego:

@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(); 
} 

Można oczywiście stworzyć @Column że odwzorowuje bar_id, ale ja nie polecam tego, jak to niby pokonuje cel JPA.

Powiązane problemy