9

我正在努力为 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")

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

eq并且ne已经在 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

  • 如果不可能,那么对于 , 之类的方法使用最 Scalaesque 的语法是eq什么ne

编辑

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

4

4 回答 4

3

我对jOOQ没有完全相同的问题,因为我使用了更冗长的运算符名称:equal,notEqual等而不是eq, ne。另一方面,valjOOQ 中有一个运算符用于显式创建绑定值,我必须用 重载它value,就像valScala 中的关键字一样。重载运算符是否适合您?我在这里记录了我在 Scala 中运行 jOOQ 的尝试:

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

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

于 2011-12-24T10:07:14.143 回答
3

Westkämper 先生 - 我正在思考这个问题,我想知道是否可以使用“跟踪器”对象,其中将扩展 Int 和 String 等基本数据类型,使其包含源信息,并且组合它们的结果将同样地,他们自己也掌握了它们的来源和组合的性质。

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

对于不等式运算符,以及对于 Ints 以及您希望从 sql 表示的任何其他内容,以及每个这些操作的相应跟踪器类,都必须覆盖定义。这将是一个小项目!

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

于 2010-09-13T17:00:53.187 回答
3

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

在此处观看视频:Christoph Wulf 的 Scala 中嵌入的类型安全 SQL

于 2010-09-09T10:18:07.277 回答
1

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

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

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

于 2010-09-10T09:56:19.490 回答