2014-05-09 8 views
7

Jak zaktualizować kilka kolumn w tabeli tabeli, zwracając całą zaktualizowaną tabelę przy użyciu śliskiego?Zręczny 2 - Zaktualizuj kolumny w tabeli i zwróć obiekt całej tabeli

Zakładając SomeTables pewne TableQuery, byś zazwyczaj napisać kwerendę tak, jeśli chcesz na przykład dodać element do tabeli (i powrót do nowo dodanego elementu)

val returnedItem = SomeTables returning SomeTables += someTable 

Jak byś zrobić to samo, jeśli chcesz zaktualizować pozycję i powrócić całość z powrotem cały element, podejrzewam byś zrobił coś takiego

val q = SomeTables.filter(_.id === id).map(x => (x.someColumn,x.anotherColumn)) returning SomeTables 
val returnedItem = q.update((3,"test")) 

Poniższy kod jednak nie działa, a ja nie widzę żadnej dokumentacji o tym, jak to zrobić jest

Zauważ, że jestem świadomy można tylko kwerendy elementu wcześniej, zaktualizuj je, a następnie za pomocą kopii na oryginalny obiekt, jednak wymaga to dużo boilerplate (i DB wycieczki również)

+0

Nie rozumiem, czy chcesz zaktualizować obiekt i zwrócić go lub chcesz zaktualizować obiekt, a następnie mają 'TableQuery' z aktualizacją po prostu zrobił, gdyby po drugie prawdopodobnie wystarczy dołączyć ".run" do zapytania, które wykonujesz. –

+0

Zakładając, że mam pełną projekcję z "klasy case" do kolumn tabeli, chcę zaktualizować podzbiór tych kolumn (tj. '.map (x => (x.someColumn, x.anotherColumn)) 'i zwróć cały zaktualizowany obiekt (zwracając' SomeTables'). Więc w powyższym przykładzie, SomeTables może mieć domyślną projekcję '*', która miałaby 5 kolumn, aktualizuję dwie z tych kolumn ('someColumn' i' anotherColumn'), jednak chcę zwrócić cały "table" (lub obiekt), który zawierałby 5 kolumn. – mdedetrich

+0

Z mojego doświadczenia początkującego nie widzę wyjścia poza aktualizacją, a następnie wybierz wiersz, metoda "aktualizuj" ty " ponowne wywołanie zwraca 'Int' (tzn. czy aktualizacja się powiodła, czy nie). Niestety nie mogłem pomóc, mam nadzieję, że ktoś z większym doświadczeniem odpowie: –

Odpowiedz

4

Ta funkcja nie jest obsługiwane w środowisku Slick (v2 lub v3-M1); chociaż nie widzę żadnego konkretnego powodu, który zabraniałby implementacji, UPDATE ... RETURNING nie jest standardową funkcją SQL (na przykład H2 nie obsługuje go: http://www.h2database.com/html/grammar.html#update). Opuszczę jako ćwiczenie dla czytelnika, aby zbadać, w jaki sposób można bezpiecznie i efektywnie emulować funkcję dla RDBMS z brakiem UDPATE ... RETURNING.

Po wywołaniu "powrocie" na scala.slick.lifted.Query, daje on JdbcInsertInvokerComponent$ReturningInsertInvokerDef. Nie znajdziesz metody update, mimo że istnieje metoda ; jednakże insertOrUpdate zwraca tylko wynik wyrażenia returning, jeśli wystąpi wstawka, zwracana jest None w celu aktualizacji, więc nie ma tutaj pomocy.

Z tego możemy wywnioskować, że jeśli chcesz korzystać z funkcji SQL UPDATE ... RETURNING, musisz albo użyć StaticQuery, albo rozwinąć własną poprawkę do wersji Slick. Można ręcznie napisać zapytania (i ponownie wdrożyć swoje projekcje tabelę jako getResult/SetParameter serializers), można też spróbować tego fragmentu kodu:

package com.spingo.slick 

import scala.slick.driver.JdbcDriver.simple.{queryToUpdateInvoker, Query} 
import scala.slick.driver.JdbcDriver.{updateCompiler, queryCompiler, quoteIdentifier} 
import scala.slick.jdbc.{ResultConverter, CompiledMapping, JdbcBackend, JdbcResultConverterDomain, GetResult, SetParameter, StaticQuery => Q} 
import scala.slick.util.SQLBuilder 
import slick.ast._ 

object UpdateReturning { 
    implicit class UpdateReturningInvoker[E, U, C[_]](updateQuery: Query[E, U, C]) { 
    def updateReturning[A, F](returningQuery: Query[A, F, C], v: U)(implicit session: JdbcBackend#Session): List[F] = { 
     val ResultSetMapping(_, 
     CompiledStatement(_, sres: SQLBuilder.Result, _), 
     CompiledMapping(_updateConverter, _)) = updateCompiler.run(updateQuery.toNode).tree 

     val returningNode = returningQuery.toNode 
     val fieldNames = returningNode match { 
     case Bind(_, _, Pure(Select(_, col), _)) => 
      List(col.name) 
     case Bind(_, _, Pure(ProductNode(children), _)) => 
      children map { case Select(_, col) => col.name } toList 
     case Bind(_, TableExpansion(_, _, TypeMapping(ProductNode(children), _, _)), Pure(Ref(_), _)) => 
      children map { case Select(_, col) => col.name } toList 
     } 

     implicit val pconv: SetParameter[U] = { 
     val ResultSetMapping(_, compiled, CompiledMapping(_converter, _)) = updateCompiler.run(updateQuery.toNode).tree 
     val converter = _converter.asInstanceOf[ResultConverter[JdbcResultConverterDomain, U]] 
     SetParameter[U] { (value, params) => 
      converter.set(value, params.ps) 
     } 
     } 

     implicit val rconv: GetResult[F] = { 
     val ResultSetMapping(_, compiled, CompiledMapping(_converter, _)) = queryCompiler.run(returningNode).tree 
     val converter = _converter.asInstanceOf[ResultConverter[JdbcResultConverterDomain, F]] 
     GetResult[F] { p => converter.read(p.rs) } 
     } 

     val fieldsExp = fieldNames map (quoteIdentifier) mkString ", " 
     val sql = sres.sql + s" RETURNING ${fieldsExp}" 
     val unboundQuery = Q.query[U, F](sql) 
     unboundQuery(v).list 
    } 
    } 
} 

Jestem pewien powyższego można poprawić; Napisałem to na podstawie mojej nieco ograniczonej wiedzy na temat wewnętrznych wersji oprogramowania Slick i działa ono dla mnie i może wykorzystywać predykcje/odwzorowania typów, które już zdefiniowałeś.

Zastosowanie:

import com.spingo.slick.UpdateReturning._ 
val tq = TableQuery[MyTable] 
val st = tq filter(_.id === 1048003) map { e => (e.id, e.costDescription) } 
st.updateReturning(tq map (identity), (1048003, Some("such cost"))) 
+2

Dzięki! Byłoby świetnie, gdyby była zaktualizowana wersja Slicka 3.0 :-) –

+0

Czy jest jakaś różnica dotycząca tego w Slick 3? – Ixx

+0

Nie zaktualizowaliśmy jeszcze wersji Slick 3; Jestem pewien, że ogólne podejście zadziała, ale prawdopodobnie API i struktury danych nieco się zmieniły. –

Powiązane problemy