Как Scala Slick переводит код Scala в JDBC?

Как Slick перевести код, например:

val q2 = for {
  c <- Coffees if c.price < 9.0
  s <- Suppliers if s.id === c.supID
} yield (c.name, s.name)
for(t <- q2) println("  " + t._1 + " supplied by " + t._2)

В JDBC?

Использует ли он Scala Virtualized? Использует ли он какой-то другой метод?

Ответ 1

Слайд-стабильный API достигает этого через то, что он называет снятым вложением. В вашем примере явно используется стабильный API (поскольку вы используете === для равенства, а не ==).

Красота Slick (и, в свою очередь, Scala) заключается в том, что - это многое достигается без использования макросов или Scala -Virtualized. (Side Note: Slick экспериментальный API использует макросы - и это позволит вам использовать == вместо === или is)

Перевод в SQL достигается с помощью:

  • Scala for синтаксис понимания, который переводится в вызовы методов. Таблицы, определенные в Slick, Монады - у них есть волшебство foreach, map, flatMap и filter, которые позволяют их выражать в циклах for ', а Scala переводит их на вызовы методов (как правильно показано в коде предоставленный другим ответом @emil-ivanov).

    Как и в обычных коллекциях Scala, for является синтаксическим сахаром для вложенных метод вызывает flatMap/map и filter; в отличие от обычных коллекций, версии Slick Table объектов map и filter возвращают представления запроса, построив его вместе с каждым условием фильтрации (if) или соедините (как в s <- Suppliers if s.id is c.supID)

    Таким образом, тип q2 не является вашей обычной коллекцией (как для понимания в Scala обычно используется для возврата), а скорее представление запроса. (Так же как Scala Option Monad также работает с for пониманиями, несмотря на не являясь "коллекцией" (таким образом, что List или map))

    Вы можете увидеть базовый запрос с помощью q2.selectStatement.

  • Scala неявный подъем - c.price не является Int, а скорее представляет собой представление значение столбца - поэтому выражение c.price < 9.0 становится c.price.<(Const(9.0)) (a Int поднимается до нужного типа), а < - это просто метод класс, который представляет c.price, a Column. Метод < не делает то, что обычно < (в случае простого Int s) - он просто возвращает представление SQL AST, соответствующее price < 9, которое становится частью SQL, который сгенерирован и отправлен в JDBC для выполнения.

Там многое еще происходит, с точки зрения деталей, но я думаю, что монада запросов и неявный подъем являются главными ингредиентами.

Ответ 2

В Scala цикл for "на самом деле не является специальной конструкцией языка, а скорее синтаксическим сахаром. Ваш первый пример

val q2 = for {
  c <- Coffees if c.price < 9.0
  s <- Suppliers if s.id === c.supID
} yield (c.name, s.name)

переводит что-то в строки:

val q2 = Coffees.withFilter(_.price < 9.0).flatMap(c =>
    Suppliers.withFilter(_.id === c.supID).map(s =>
        (c.name, s.name)
    )
)

Теперь flatMap, map, withFilterforeach) фактически не фильтруют коллекцию, а скорее собирают то, что происходит в AST (абстрактное дерево синтаксиса), которое затем обрабатывается Slick для перевода в SQL.

Кроме того, c.price, c.supID на самом деле являются Slick column s, методы <, >, === (и т.д.) не возвращают bool, но также собирают сравнение, который позже передается для преобразования в SQL.

Это беседа создателей, где большая часть этого описана (правильно).