SQL DSL для Scala - PullRequest
       33

SQL DSL для Scala

9 голосов
/ 09 сентября 2010

Я пытаюсь создать SQL DSL для Scala. DSL является расширением Querydsl , который является популярным уровнем абстракции Query для Java.

Сейчас я борюсь с очень простыми выражениями вроде следующего

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

Поскольку Querydsl уже поддерживает модель выражений, которую можно использовать здесь, я решил обеспечить преобразование объектов Proxy в выражения Querydsl. Для использования прокси я создаю такой экземпляр

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

var user = alias(classOf[User])

С помощью следующих неявных преобразований я могу преобразовывать экземпляры прокси и цепочки вызовов свойств прокси в выражения Querydsl

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

Теперь я могу создавать выражения, подобные этому

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")

Я борюсь со следующей проблемой.

eq и ne уже доступны как методы в Scala, поэтому преобразования не запускаются при их использовании

Эта проблема может быть обобщена следующим образом. При использовании имен методов, которые уже доступны в таких типах Scala, как eq, ne, launchWith и т. Д., Необходимо использовать какой-либо вид экранирования для запуска неявных преобразований.

Я рассматриваю следующее

Прописные буквы

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

Это, например, подход в Circumflex ORM , очень мощной среде ORM для Scala с аналогичными целями DSL. Но этот подход не согласуется с ключевыми словами запроса (выберите, откуда, где и т. Д.), Которые в Querydsl строчные.

Некоторые префиксы

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

Контекст использования предиката выглядит примерно так

var user = alias(classOf[User])

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

Видите ли вы лучшие варианты или другой подход для построения SQL DSL для Scala?

Таким образом, вопрос сводится к двум случаям

  • Можно ли вызвать неявное преобразование типов при использовании метода, который существует в суперклассе (например, eq)

  • Если это невозможно, то какой синтаксис Scalaesque наиболее подходит для таких методов, как eq, ne.

EDIT

Мы получили поддержку Scala в Querydsl, используя псевдонимы и escape-синтаксис на основе $ -prefix. Вот сообщение в блоге о результатах: http://blog.mysema.com/2010/09/querying-with-scala.html

Ответы [ 4 ]

3 голосов
/ 13 сентября 2010

Мистер Весткэмпер - я размышлял над этой проблемой, и мне было интересно, можно ли будет использовать объекты 'tracer', где базовые типы данных, такие как Int и String, будут расширены таким образом, чтобы они содержали исходную информацию и результатыих объединение также будет содержать в себе их источники и природу комбинации.

Например, ваш метод user.firstName вернет TracerString, который расширяет String, но также указывает, что String соответствует столбцув отношении.Метод == будет перезаписан таким образом, что он возвращает EqualityTracerBoolean, который расширяет Boolean.Это сохранит стандартную семантику Scala.Однако конструктор для EqualityTracerBoolean запишет тот факт, что результат выражения был получен путем сравнения столбца по отношению к строковой константе.Ваш метод 'where' может затем проанализировать объект EqualityTracerBoolean, возвращенный условным выражением, оцененным по фиктивному аргументу, чтобы получить выражение, использованное для его создания.

Для операторов неравенства потребуется переопределить def, так кака также плюсы и минусы, для Ints, и все, что вы хотели бы представить из sql, и соответствующие классы трассировки для каждого из них.Это было бы немного похоже на проект!

В любом случае, я решил не беспокоиться, и вместо этого использовал squeryl .

3 голосов
/ 09 сентября 2010

На Scala Days была очень хорошая беседа: Кристоф Вульф: безопасный для типов SQL, встроенный в Scala.

Смотрите видео здесь: Типобезопасный SQL, встроенный в Scala, Кристоф Вульф

2 голосов
/ 24 декабря 2011

У меня не было точно такой же проблемы с jOOQ , так как я использую более подробные имена операторов: equal, notEqual и т. Д. Вместо eq, ne.С другой стороны, в jOOQ есть оператор val для явного создания значений связывания, которые мне пришлось перегрузить с помощью value, так как val является ключевым словом в Scala.Является ли перегрузка операторов вариантом для вас?Я задокументировал свои попытки запуска jOOQ в Scala здесь:

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

Как и вы, я также думал о том, чтобы использовать все ключевые слова в основной версии (включая SELECT, * 1016)*, так далее).Но это оставляет открытым вопрос о том, должны ли «составные» ключевые слова быть разделены на два вызова метода или связаны подчеркиванием: GROUP().BY() или GROUP_BY().WHEN().MATCHED().THEN().UPDATE() или WHEN_MATCHED_THEN_UPDATE().Поскольку результат не совсем удовлетворителен, я полагаю, что для такого исправления не стоит нарушать обратную совместимость, даже если опция вызова двух методов выглядела бы очень хорошо в Scala, так как . и () могут бытьопущено.Так что, может быть, jOOQ и QueryDSL должны быть «обернуты» (а не «расширены») специальным Scala-API?

1 голос
/ 10 сентября 2010

Как насчет декомпиляции байт-кода во время выполнения?Я начал писать такой инструмент:

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

Я знаю, что это взлом, поэтому, пожалуйста, не голосуйте -1 :-) Я просто хотел упомянуть об этом.Это относительно новый подход.Вместо того, чтобы декомпилировать во время выполнения, может быть возможно сделать это во время компиляции, используя процессор аннотаций, не уверенный, если это возможно, используя Scala (и не уверен, действительно ли это возможно с Java, но Project Lombok кажетсясделать что-то подобное).

...