2015-11-19 13 views
9

Diciamo che ho due tabelle seguenti:Come faccio a fare riferimento alle colonne mappate solo per i join 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; 
} 

e voglio scrivere una query JPQL che cambia alcuni valori Bar.flag sulla base di un insieme di Foo ID.

Se questo fosse SQL pianura mi piacerebbe scrivere qualcosa di simile:

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

Non si può tradurre questo a JPQL però, perché la colonna bar_id non è mappato a una proprietà dell'entità.

Come bar_id come non è mappato direttamente sull'unità , come posso ottenere questo tipo di query in JPQL?

+0

dov'è la collezione? –

+0

non risponde? http://stackoverflow.com/questions/3629259/how-do-i-do-a-jpql-subquery –

+0

@sashok_bg Non l'ho provato, ma non penso che lo farebbe perché non ho il chiavi esterne grezzi mappate nell'entità ('p.country_id' nel loro esempio) – SCdF

risposta

3

Se si desidera una query di aggiornamento di massa di lavoro, il seguente dovrebbe funzionare:

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

L'idea è quella di lavorare con le entità, quando si effettua la selezione secondaria.

0

Non è possibile accedere direttamente allo bar_id. È necessario eseguire un join "extra" tra Foo e Bar per utilizzare le informazioni id dall'entità Bar.

Quindi, la query JPQL sarà simile a questo:

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

Una delle caratteristiche chiave di APP è astraendo i database dettagli essenziali (come chiavi esterne) dal vostro codice Java. Ci sono diversi modi di raggiungere la query che si sta delineando, qui ci sono un paio:

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

o, alla rinfusa:

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

Si noti che tale aggiornamento non cambierà qualsiasi entità recuperata all'interno della stessa transazione. Fare

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

non cambierà l'oggetto Foo effettivo.

Il mio modo preferito di fare questo sarebbe qualcosa di simile:

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

Si potrebbe comunque creare un @Column che mappa il bar_id, ma io non consiglierei questo, come che tipo di sconfitte lo scopo di JPA.

Problemi correlati