2015-06-15 8 views
16

Chcielibyśmy uruchomić łatkę/częściową UPDATE przy pomocy Slick (3.0.0), aby zmodyfikować tylko niektóre pola w rekordzie. Dokładnie to, które pola będą dokładnie aktualizowane, będzie znane tylko w czasie wykonywania.W jaki sposób uruchamiasz aktualizację poprawki/częściowej bazy danych w Scala Slick?

Na przykład dla REST PATCH request.

Obecnie uruchamiamy najpierw SELECT, aby uzyskać oryginalny rekord, a następnie uruchomić UPDATE, ale byłoby fajniej zrobić to w pojedynczej instrukcji SQL.

coś takiego:

def patchPerson(name: Option[String], age: Option[Int]) = { 
    people.filter(_.name === "M Odersky") 
     .map(p => 
      (name, age) match { 
       case (Some(_), Some(_)) => (p.name, p.age) 
       case (Some(_), None) => (p.name) 
       case (None , Some(_)) => (p.age) 
      } 
     ) 
     .update(
      (name, age) match { 
       case (Some(_), Some(_)) => (name.get, age.get) 
       case (Some(_), None) => (name.get) 
       case (None , Some(_)) => (age.get) 
      } 
     ) 
} 

(proszę ignorować brzydkie kod tutaj)

Powyższe nie skompilować z następującym komunikatem o błędzie:

Nie dopasowywania Kształt znalezionego . Slick nie wie, jak zmapować podane typy . Możliwe przyczyny: T w tabeli [T] nie pasuje do projekcji * . Lub używasz nieobsługiwanego typu w kwerendzie (np. Scala Lista). Wymagany poziom: slick.lifted.FlatShapeLevel typ Źródło: Object rozpakowywane typ: T pakiety typu: G

oraz:

nie wystarczająco dużo argumentów dla metody mapie: (niejawny kształt: slick.lifted .Shape [_ <: slick.lifted.FlatShapeLevel, Object, T, G]) slick.lifted.Query [G, T, Seq]. Nieokreślony kształt parametru wartości.

Zakładam, to dlatego, że Slick oczekuje długość krotka i typ pasujące wyniki dla obu funkcji filter i update.

Próbowaliśmy używać klasy Slick heterogeneous list, ale wydaje się również, że oczekiwana długość i typy pasują do siebie.

Czy istnieje sposób na zapisanie tego w wersji Slick, abyśmy mogli zaktualizować dowolną liczbę pól w rekordzie za pomocą jednego wywołania bazy danych?

+0

Czy znalazłeś rozwiązanie? – 757071

+0

A co z uruchomieniem natywnego kodu SQL? –

+0

Dodałem nagrodę, ponieważ mam ten sam problem, co autor. @LukasEder, który nie działałby dobrze, gdybyś nie wiedział, które klucze łatasz. Na przykład. Mam do aktualizacji dużą klasę Case użytkownika i chciałbym mieć ogólny punkt końcowy PATCH, do którego mogę po prostu wysłać jedno zaktualizowane pole. – Jeroen

Odpowiedz

1

Mój najlepszy przypuszczenie byłoby uruchomić plain SQL query

Nawet jeśli zapytanie SQL ma 2 części, relacyjny system zarządzania bazą danych (PostgreSQL, MySQL, itp) jest w stanie dostroić kwerendy pod kapturami.

Nie jestem pewien, czy w tym przypadku Slick jest w stanie zoptymalizować, ale w kilku przypadkach sam też jest optimizes the queries.

Typowa zmiana:

def updateRecord(id: Long, field1: Int) = { 
    db.withSession { 
     self.filter(_.id === id).map(_.field1).update(field1) 
    } 
} 

Robi typ aktualizacji wymaga trochę więcej logiki jak ty. Nie myśl, że można to uprościć, jeśli wiesz tylko, w których polach chcesz zmienić.Ale można wymusić aktualizację, wykorzystując istniejącą wartość na polu w rekordzie jako odwrotu (może prowadzić do większej liczby aktualizacji na DB niż powinno)

def updateRecord(id: Long, field1: Option[Int], field2: Option[Int]) = { 
    db.withSession { 
     self.filter(_.id === id).map(_.field1, _.field2).update(field1.getOrElse(existingValue1), field2.getOrElse(existingValue2)) 
    } 
} 
+0

Zwykły SQL rzeczywiście – cvogt

+0

Wierzę również, że zwykły SQL jest obecnie właściwym rozwiązaniem tego problemu. Firma Slick prawdopodobnie mogłaby zoptymalizować jedną aktualizację na pole do jednej aktualizacji SQL, jeśli zaproponuje się ją jako alternatywę. Jedynym prawdziwym problemem jest uzyskanie istniejącej wartości VALueX bez dodatkowego zapytania. Ideą tego pytania jest uniknięcie SELECT + UPDATE. Naprawdę szkoda, że ​​funkcja aktualizacji nie otrzymuje bieżących wartości. –

2

Dlaczego nie dopasowywanie do wzorca przed konstruowania zapytania aktualizacji?

+0

ładne, tks. było użyteczne –

0

Masz już odpowiedzi napisane przez @ pedrorijo91 i @thirstycow, ale spróbuję wyjaśnić, dlaczego to nie działa.

Nie używam slick 3, ale domyślam się, ponieważ funkcja map nie zwraca spójnego typu, aby uruchomić aktualizację. Jako eksperyment myślowy, jeśli przerwiesz telefon na mapie, jak myślisz, jaki będzie typ?

val partialQuery:??? = people.filter(_.name === "M Odersky") 
     .map(p => 
      (name, age) match { 
       case (Some(_), Some(_)) => (p.name, p.age) 
       case (Some(_), None) => (p.name) 
       case (None , Some(_)) => (p.age) 
      } 
     ); 

val fullQuery:??? = partialQuery.update { 
     (name, age) match { 
      case (Some(_), Some(_)) => (name.get, age.get) 
      case (Some(_), None) => (name.get) 
      case (None , Some(_)) => (age.get) 
     }  
} 

Łyżka zwraca różne "kształty" w czasie kompilacji, które zostaną zmienione na Dowolny typ.

+0

Po prostu chcę dodać, że jest tak naprawdę trzecie rozwiązanie, które polega na pisaniu makra. Nie zamierzam tu wchodzić w szczegóły, ale nie będzie to wyglądało jak twoje rozwiązanie (ale może fajniej?) – critium

Powiązane problemy