【问题标题】:SQL DSL for ScalaScala 的 SQL DSL
【发布时间】:2011-04-10 04:03:54
【问题描述】:

我正在努力为 Scala 创建 SQL DSL。 DSL 是对Querydsl 的扩展,Querydsl 是一种流行的 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")

我正在努力解决以下问题。

eqne 在 Scala 中已作为方法提供,因此在使用时不会触发转换

这个问题可以概括如下。当使用 Scala 类型中已经存在的方法名称时,例如 eq、ne、startsWith 等,需要使用某种转义来触发隐式转换。

我正在考虑以下内容

大写

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

例如Circumflex ORM 中的方法,这是一个非常强大的用于 Scala 的 ORM 框架,具有类似的 DSL 目标。但是这种方式会与Querydsl中小写的查询关键字(select、from、where等)不一致。

一些前缀

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

对于 Scala 的 SQL DSL 构造,您是否看到了更好的选择或不同的方法?

所以问题基本上归结为两种情况

  • 使用超类中存在的方法时是否可以触发隐式类型转换(例如eq

  • 如果不可能,最适合用于eqne 等方法的 Scalaesque 语法是什么。

编辑

我们通过使用别名实例和基于 $-prefix 的转义语法在 Querydsl 中获得了 Scala 支持。这是关于结果的博客文章:http://blog.mysema.com/2010/09/querying-with-scala.html

【问题讨论】:

  • 所以问题基本上归结为两种情况 a) 使用超类中存在的方法时是否可以触发隐式类型转换(例如eq) & b) 如果是不可能,对于eqne 等方法,最适合使用 Scalaesque 语法是什么?我理解正确吗?
  • @ponzao 你在读我的心!我冒昧地用你的“归结”来结束我的问题。
  • 如果您不是为了它自己而构建它,请查看 ScalaQuery,Scala 的类型安全数据库查询 API:github.com/szeiger/scala-query
  • @Fabian,我知道 ScalaQuery,它很有趣,但它并不是真正的 SQL 样式查询 DSL
  • 最近基于宏的努力是github.com/jonifreeman/sqltyped

标签: scala dsl querydsl


【解决方案1】:

在 Scala Days 上有一个非常好的演讲:Christoph Wulf 在 Scala 中嵌入类型安全的 SQL。

在此处观看视频:Type-safe SQL embedded in Scala by Christoph Wulf

【讨论】:

  • 感谢您的指点。如果我正确理解视频,则 SQL 集成基于源预解析。我正在寻找一种没有任何准备的方法。
  • 这看起来很有趣,不知道实现的进展如何。
【解决方案2】:

Westkämper 先生 - 我正在思考这个问题,我想知道是否可以使用“跟踪器”对象,其中基本数据类型(如 Int 和 String)将被扩展,以便它们包含源信息,以及将它们组合起来同样会在它们自身内部保留它们的来源和组合的性质。

例如,您的 user.firstName 方法将返回一个 TracerString,它扩展了 String,但也表明该 String 对应于关系中的一列。 == 方法将被覆盖,使其返回扩展布尔值的 EqualityTracerBoolean。这将保留标准的 Scala 语义。但是,EqualityTracerBoolean 的构造函数会记录这样一个事实,即表达式的结果是通过比较关系中的列与字符串常量得出的。然后,您的 'where' 方法可以分析由对虚拟参数求值的条件表达式返回的 EqualityTracerBoolean 对象,以便推导出用于创建它的表达式。

对于不等式运算符,以及对于 Ints 以及您希望从 sql 表示的任何其他内容,必须重写 defs,并为每个这些操作提供相应的跟踪器类。这将是一个项目!

无论如何,我决定不打扰,而是使用squeryl

【讨论】:

  • 感谢您的详尽回答。我相信 Thomas Mueller 在 H2 / JaQu 中做过类似的事情。我不确定是否可以扩展 Scala 中的布尔等。这是一种有趣的方法,但确实很费力。
【解决方案3】:

jOOQ 没有完全相同的问题,因为我使用了更详细的运算符名称:equalnotEqual 等,而不是 eqne。另一方面,jOOQ 中有一个val 运算符用于显式创建绑定值,我不得不用value 重载它,因为val 是Scala 中的一个关键字。重载运算符是否适合您?我在这里记录了我在 Scala 中运行 jOOQ 的尝试:

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

和你一样,我也考虑过在主要版本中将所有关键字都大写(包括SELECTFROM 等)。但这将留下一个悬而未决的问题,即是否应该将“复合”关键字拆分为两个方法调用,或者通过下划线连接:GROUP().BY()GROUP_BY()WHEN().MATCHED().THEN().UPDATE()WHEN_MATCHED_THEN_UPDATE()。由于结果不是很令人满意,我想不值得为这样的修复破坏向后兼容性,即使两个方法调用选项在 Scala 中看起来非常非常好,因为 .() 可以省略。所以也许,jOOQ 和 QueryDSL 都应该由专用的 Scala-API “包装”(而不是“扩展”)?

【讨论】:

    【解决方案4】:

    在运行时反编译字节码怎么样?我开始写这样一个工具:

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

    我知道这是一个 hack,所以请不要投票 -1 :-) 我只是想提一下。这是一种相对新颖的方法。与其在运行时反编译,不如在编译时使用注释处理器进行反编译,不确定是否可以使用 Scala(并且不确定 Java 是否真的可以,但Project Lombok 似乎做了类似的事情) .

    【讨论】:

    • 我不是 hack 并且可以使用本地运算符,但我认为这要困难得多,反编译的结果可能取决于使用的编译器。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-05
    • 2017-05-20
    • 2017-12-23
    • 2017-09-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多