Scala SQL DSL(内部/外部)

时间:2013-03-26 17:36:45

标签: sql scala dsl

我一直在研究scala主要是关于如何构建类似于C#LINQ / SQL的DSL。使用C#LINQ查询提供程序后,很容易引入我们自己的自定义查询提供程序,它将LINQ查询转换为我们自己的专有数据存储脚本。我在scala中寻找类似的东西。

 val query = select Min(Close), Max(Close)
 from   StockPrices 
 where  open > 0 

首先,这甚至可以使用内部DSL在scala中实现。

对此方面的任何想法/想法都表示高度赞赏。

我在scala领域仍然是新手,但他开始研究Scala MetaProgramming&油滑。我对Slick的抱怨是我想让我的DSL接近SQL查询 - 类似于上面的语法。

3 个答案:

答案 0 :(得分:3)

没有办法让内部DSL(当前版本)与您提供的示例完全相同。

使用我仍然来自this answer的宏,我能得到的最近(相对较快)是:

select(Min(StockPrices.Open), Max(StockPrices.Open))
  .from(StockPrices)

real解决方案需要相当长的时间来创建。如果你愿意这样做,你可以使用宏(不是一个简单的主题)走得很远。

如果你真的想要完全相同的语法,我推荐像XText这样的东西,允许你用“免费”的基于eclipse的编辑器创建一个DSL。

上面示例所需的代码(我没有提到上面提到的宏):

trait SqlElement {
  def toString(): String
}

trait SqlMethod extends SqlElement {
  protected val methodName: String
  protected val arguments: Seq[String]

  override def toString() = {
    val argumentsString = arguments mkString ","
    s"$methodName($argumentsString)"
  }
}

case class Select(elements: Seq[SqlElement]) extends SqlElement {
  override def toString() = s"SELECT ${elements mkString ", "}"
}

case class From(table: Metadata) extends SqlElement {
  private val tableName = table.name
  override def toString() = s"FROM $tableName"
}
case class Min(element: Metadata) extends SqlMethod {
  val methodName = "Min"
  val arguments = Seq(element.name)
}
case class Max(element: Metadata) extends SqlMethod {
  val methodName = "Max"
  val arguments = Seq(element.name)
}

class QueryBuilder(elements: Seq[SqlElement]) {
  def this(element: SqlElement) = this(Seq(element))

  def from(o: Metadata) = new QueryBuilder(elements :+ From(o))
  def where(element: SqlElement) = new QueryBuilder(elements :+ element)
  override def toString() = elements mkString ("\n")
}

def select(args: SqlElement*) = new QueryBuilder(Select(args))

trait Column
object Column extends Column

object tables {

  object StockPrices$ {
    val Open: Column = Column
    val Close: Column = Column
  }
  val StockPrices = StockPrices$
}

然后使用它:

import tables._
import StockPrices._

select(Min(StockPrices.Open), Max(StockPrices.Open))
  .from(StockPrices)
  .toString

答案 1 :(得分:1)

这是一个令人钦佩的项目,但已经开始实施并且可以在一般版本中使用。

我当然在谈论Slick

答案 2 :(得分:1)

如果Scala / Java互操作性对您来说不是一个问题,并且如果您愿意使用内部DSL与您建议的语法相比有几个语法怪癖,那么jOOQ是成长为Slick的流行替代品。 jOOQ manual

中的一个示例
for (r <- e
    select (
      T_BOOK.ID * T_BOOK.AUTHOR_ID,
      T_BOOK.ID + T_BOOK.AUTHOR_ID * 3 + 4,
      T_BOOK.TITLE || " abc" || " xy"
    )
    from T_BOOK
    leftOuterJoin (
      select (x.ID, x.YEAR_OF_BIRTH)
      from x
      limit 1
      asTable x.getName()
    )
    on T_BOOK.AUTHOR_ID === x.ID
    where (T_BOOK.ID <> 2)
    or (T_BOOK.TITLE in ("O Alquimista", "Brida"))
    fetch
) {
  println(r)
}