【问题标题】:Scala SQL DSL (Internal/External)Scala SQL DSL(内部/外部)
【发布时间】:2013-03-26 17:36:45
【问题描述】:

我一直在研究 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。我对 Slick 的抱怨是我想让我的 DSL 接近 SQL 查询——类似于上面的语法。

【问题讨论】:

  • 生成的SQL是什么样子的? Open,CloseOpen.Close 是什么意思? openStockPrices 的属性吗?
  • 对不起,这是一个错字,我已经修正了我原来的帖子。我会将这个查询翻译成我们专有的脚本语言。但是,我根据您的代码示例得到了这个想法并开始阅读宏,让我们看看我离这里有多远。

标签: sql scala dsl


【解决方案1】:

没有办法拥有与您提供的示例完全相同的内部 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

【讨论】:

  • 看到宏也会很有趣。我们想在2014年和jOOQ一起朝着这样的方向发展……
  • 我用宏回答的问题链接在答案中,这里是答案的直接链接:stackoverflow.com/a/14968212/596816
  • 谢谢!对不起,不知道我怎么错过了
【解决方案2】:

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

当然,我说的是Slick

【讨论】:

    【解决方案3】:

    如果 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)
    }
    

    【讨论】:

      猜你喜欢
      • 2023-04-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-10
      • 1970-01-01
      • 1970-01-01
      • 2012-06-13
      • 1970-01-01
      相关资源
      最近更新 更多