2016-01-23 23 views
5

Questa domanda è di circa chiazza di petrolio 3.0 o 3.1 (io sono flessibile, a tale proposito)Scala + Slick 3: Inserimento il risultato di una query in un'altra tabella

devo una query intermedia che mi processo con map, for, ecc per ottenere il risultato che voglio. Alla fine ho un

val foo: DBIOAction[Seq[MySchema.Bar], NoStream, Effect.Read] 

Ora ho un val bar: TableQuery[MySchema.Bar] e voglio inserire foo ad esso.

Se foo sarebbe un Seq, potrei semplicemente fare bar ++= foo, ma non lo è.

L'unico modo che ho trovato è di materializzare il risultato attendendolo. Ti piace questa

val query = (bar ++= Await.result(db.run(foo), Duration.Inf)) 

Ovviamente query deve essere eseguito ad un certo punto con db.run. Ma ora ho due run-DB. Non sarebbe meglio avere tutto in un'unica corsa?

C'è un modo migliore per farlo?

+0

'bar + = foo'? Ma dovresti comunque eseguire 'db.run' a prescindere dal fatto che sia' + = 'o' ++ = '... (?) – kornfridge

+0

Penso che ++ = con effetti collaterali sia la slick2 api, mentre la slick3 è più funzionale, quindi richiede esplicitamente un 'db.run' – kornfridge

+0

Sì il risultato di ++ = è ora una query che deve essere eseguita. Ho modificato la domanda in questo punto. Ma devo fare due chiamate db.run per questa attività. – masgo

risposta

4

DBIOAction ha map/flatMap funzioni, in modo da poter scrivere qualcosa di simile

val insertAction = foo.flatMap(bar ++= _) 

Il insertAction sarà di tipo DBIOAction[Int, NoStream, Effect.Write] o qualcosa del genere (io non sono del tutto sicuro su Int e l'effetto), quindi è possibile eseguirlo sul DB come qualsiasi DBIOAction utilizzando db.run; quello che ottieni è un futuro dell'interrogazione generale e inserisci i risultati.

+0

Il 'flatMap' fa il trucco. Alla fine ho solo una DBIOAction rimasta che devo eseguire. Ho imbattuto in un'altra cosa con questo argomento: quando dovrei fare il db.run()? Ecco un esempio di 'val foo = db.run (person.result.map (_. map {p => tr (td (p.id))})) 'Posso farlo in questo modo, oppure potrei terminare la parte' db.run' dopo '.result '. Entrambi i metodi mi danno lo stesso 'Futuro', c'è qualche differenza in termini di prestazioni? – masgo

+2

In generale si desidera lavorare a livello 'DBIO' il più lontano possibile; quando chiami 'db.run' slick eseguirà quante più query sono necessarie utilizzando il proprio contesto di esecuzione e ti restituirà il' Futuro'. Penso che ci sia una potenziale differenza nelle prestazioni se lo fai, dato che slick ottimizzerà l'uso dei thread rispetto all'accesso ai DB. –

1

Le domande avevano già una risposta, ma sono venuto qui cercando una soluzione diversa, quindi forse questo sarà utile ad altre persone.

Come dice @Aldo, vogliamo lavorare a livello DBIO per quanto possibile, ma vorrei andare oltre e dire che si dovrebbe lavorare a livello Query per quanto possibile, in quanto questo viene compilato in una singola istruzione SQL che può essere inviato al database.

Ad esempio, un inserto da una selezione deve essere compilato a INSERT INTO table1 SELECT.... Se si utilizza più DBIOS utilizzando flatMap, come suggerito, questo verrà compilato in un SELECT, i valori verranno portati in memoria e quindi verrà compilata un'istruzione INSERT, interpolando i valori in una stringa e questa nuova query verrà inviata al Banca dati. In realtà questo può essere molto più lento se la tua selezione restituisce molti risultati e può svuotare la memoria nel peggiore dei casi.

Quindi per compilare una cosa del genere in una singola query è possibile scrivere:

val bar: TableQuery[MySchema.Bar] 

val foo: Query[MySchema.Bar, Bar, Seq] 

val insert: DBIO[Int] = bar.forceInsertAll(foo)