2010-09-09 8 views
9

Mam problemy z utworzeniem SQL DSL dla Scala. DSL jest rozszerzeniem do Querydsl, która jest popularną warstwą abstrakcji zapytania dla Javy.SQL DSL dla Scala

walczę teraz z bardzo prostych wyrażeń jak na poniższym

user.firstName == "Bob" || user.firstName == "Ann" 

Jak Querydsl obsługuje już model ekspresji, który może być używany tutaj postanowiłem zapewnić konwersje z Proxy sprzeciwia się Querydsl wyrażeń. W celu korzystania z serwerów proxy utworzyć instancję jak ten

import com.mysema.query.alias.Alias._ 

var user = alias(classOf[User]) 

z następującymi niejawnych konwersji można przekonwertować przypadki proxy i proxy łańcuchy połączeń nieruchomość do Querydsl wyrażeń

import com.mysema.query.alias.Alias._ 
import com.mysema.query.types.expr._ 
import com.mysema.query.types.path._ 

object Conversions {   
    def not(b: EBoolean): EBoolean = b.not()   
    implicit def booleanPath(b: Boolean): PBoolean = $(b);   
    implicit def stringPath(s: String): PString = $(s);   
    implicit def datePath(d: java.sql.Date): PDate[java.sql.Date] = $(d);   
    implicit def dateTimePath(d: java.util.Date): PDateTime[java.util.Date] = $(d);   
    implicit def timePath(t: java.sql.Time): PTime[java.sql.Time] = $(t);    
    implicit def comparablePath(c: Comparable[_]): PComparable[_] = $(c);   
    implicit def simplePath(s: Object): PSimple[_] = $(s);   
} 

Teraz mogę zbudować wyrażenia jak to

import com.mysema.query.alias.Alias._ 
import com.mysema.query.scala.Conversions._ 

var user = alias(classOf[User]) 
var predicate = (user.firstName like "Bob") or (user.firstName like "Ann") 

Mam problem z następującym problemem.

eq i ne są już dostępne jako metod w Scala, więc konwersje nie są wyzwalane, gdy są one wykorzystywane

Problem ten można uogólnić jak poniżej. Podczas używania nazw metod, które są już dostępne w typach Scala, takich jak eq, ne, startsWith itp., Należy użyć pewnego rodzaju escaping, aby wyzwolić niejawne konwersje.

Zastanawiam następujące

UPPERCASE

var predicate = (user.firstName LIKE "Bob") OR (user.firstName LIKE "Ann") 

to jest na przykład w Circumflex ORM podejście, bardzo potężny framework ORM dla Scala z podobnym DSL ma. Ale takie podejście byłoby niespójne ze słowami kluczowymi zapytania (select, from, where itp.), Które są pisane małymi literami w Querydsl.

Niektóre prefiks

var predicate = (user.firstName :like "Bob") :or (user.firstName :like "Ann") 

Kontekst użycia kwantyfikatorów jest coś takiego

var user = alias(classOf[User]) 

query().from(user) 
    .where( 
     (user.firstName like "Bob") or (user.firstName like "Ann")) 
    .orderBy(user.firstName asc) 
    .list(user); 

Czy widzisz lepsze opcje lub inne podejście do budowy SQL DSL dla Scala?

Więc pytanie w zasadzie sprowadza się do dwóch przypadków

  • Czy jest możliwe aby wywołać niejawna konwersja typu przy użyciu metody, która istnieje w super klasy (np eq)

  • jeśli to nie jest możliwe, jaka byłaby najbardziej scalaesowa składnia do zastosowania dla metod takich jak eq, ne.

EDIT

Mamy wsparcie Scala w Querydsl pracy za pomocą wystąpień alias i składni ucieczki oparciu $ -prefix. Oto blog na temat wyników: http://blog.mysema.com/2010/09/querying-with-scala.html

+1

Więc pytanie w zasadzie sprowadza się do dwóch przypadków a) Czy jest możliwe wyzwolenie niejawnej konwersji typu, gdy używa się metody, która istnieje w super klasie (np. 'Eq') i b) Jeśli nie jest możliwe, co by było być najbardziej scalaeskową składnią do użycia w takich metodach jak 'eq',' ne'. Czy rozumiem poprawnie? – ponzao

+0

@ponzo, czytasz mój umysł! Wziąłem wolność, aby zakończyć moje pytanie z waszym "sprowadzonym". –

+0

Jeśli nie budujesz tego dla samej rzeczy, sprawdź ScalaQuery, bezpieczny dla bazy danych interfejs API zapytania dla Scala: http://github.com/szeiger/scala-query –

Odpowiedz

3

W Scala Days odbyła się bardzo dobra rozmowa: Bezpieczny typ SQL osadzony w Scali przez Christopha Wulfa.

Zobacz film tutaj: Type-safe SQL embedded in Scala by Christoph Wulf

+1

Dzięki za wskaźnik. Jeśli dobrze rozumiem wideo, integracja SQL opiera się na wstępnym analizie źródła. Szukam podejścia bez żadnych preparsing. –

+0

to naprawdę wygląda interesująco, nie jestem pewny jak przebiega implementacja. – zinking

1

co z dekompilacji kodu bajtowego w czasie wykonywania? Zacząłem pisać takiego narzędzia:

http://h2database.com/html/jaqu.html#natural_syntax

wiem, że to hack, więc proszę nie głosować -1 :-) Chciałem wymienić go. To stosunkowo nowe podejście. Zamiast dekompilować w czasie wykonywania, może być możliwe zrobienie tego podczas kompilacji za pomocą procesora adnotacji, nie ma pewności, czy jest to możliwe przy użyciu Scala (i nie jest pewne, czy jest to naprawdę możliwe z Javą, ale wydaje się, że Project Lombok robi coś takiego).

+0

Nie jest to włamanie i umożliwia używanie natywnych operatorów, ale myślę, że jest to znacznie trudniejsze, a wyniki dekompilacji mogą zależeć od używanego kompilatora. –

3

Mr Westkämper - Zastanawiałem się nad tym problemem i zastanawiałem się, czy możliwe byłoby użycie obiektów "śledzących", gdzie podstawowe typy danych, takie jak Int i String, zostałyby rozszerzone tak, że zawierały informacje źródłowe, a wyniki połączenie ich będzie również posiadać w sobie ich źródła i charakter kombinacji.

Na przykład, twoja metoda user.firstName zwróci TracerString, który rozszerza String, ale który również wskazuje, że String odpowiada kolumnie w relacji. Metoda == zostanie nadpisana tak, że zwraca EqualityTracerBoolean, który rozszerza Boolean. Zachowałoby to standardową semantykę Scala. Jednak konstruktor EqualityTracerBoolean zapisałby fakt, że wynik wyrażenia został uzyskany przez porównanie kolumny w relacji do stałej łańcuchowej. Twoja metoda "where" może następnie analizować obiekt EqualityTracerBoolean zwracany przez wyrażenie warunkowe obliczone na podstawie atrapa w celu wyprowadzenia wyrażenia użytego do jego utworzenia.

Musi być nadpisane defs dla operatorów nierówności, plus i minus, dla Ints i cokolwiek innego chciałbyś reprezentować z sql i odpowiednie klasy znaczników dla każdego z nich. To byłby pewien projekt!

W każdym razie postanowiłem nie zawracać sobie głowy i zamiast tego użyć squeryl.

+0

Dzięki za wyczerpującą odpowiedź. Wierzę, że Thomas Mueller zrobił coś takiego w H2/JaQu. Nie jestem pewien, czy Boolean itp. W Scala może zostać przedłużony. Jest to interesujące podejście, ale bardzo pracochłonne. –

2

nie mam dokładnie ten sam problem z jOOQ, jak używam nieco więcej komunikatów nazwy operatora: equal, notEqual itp zamiast eq, ne. Z drugiej strony, w jOOQ jest operator val dla jawnego tworzenia wartości powiązań, które musiałem przeciążać z value, ponieważ val jest słowem kluczowym w Scali. Czy przeciążanie operatorów jest dla ciebie opcją? I udokumentowane moje próby prowadzenia jOOQ w Scala tutaj:

http://lukaseder.wordpress.com/2011/12/11/the-ultimate-sql-dsl-jooq-in-scala/

Tak jak ty, ja też myślałem o czerpanie wszystkie słowa kluczowe w wersji głównej (w tym SELECT, FROM, etc). Ale to pozostawia otwarte pytanie o to, czy słowa kluczowe "złożone" powinny być podzielone na dwa wywołania metod, czy połączone podkreśleniem: GROUP().BY() lub GROUP_BY().WHEN().MATCHED().THEN().UPDATE() lub WHEN_MATCHED_THEN_UPDATE(). Ponieważ wynik nie jest satysfakcjonujący, myślę, że nie warto łamać kompatybilności wstecznej dla takiej poprawki, nawet jeśli opcja dwóch metod wywołania wyglądałaby bardzo dobrze w Scali, ponieważ można pominąć . i (). Może więc jOOQ i QueryDSL powinny być "zapakowane" (w przeciwieństwie do "rozszerzonego") przez dedykowany Scala-API?