scala dsl
有大量用Scala原生编写SQL API。 曼努埃尔·伯恩哈特(Manuel Bernhardt)在其帖子中总结了一个不错的收藏 。 在此Stack Overflow问题中可以看到Scala SQL API的另一个集合。
我们要特别关注的一个API是ScalikeJDBC ( 获得许可的ASL 2.0 ),该API最近发布了类似于jOOQSQL查询DSL API。 请参阅此处的完整文档:
http://scalikejdbc.org/documentation/query-dsl.html
几个例子:
val orders: List[Order] = withSQL {
select
.from(Order as o)
.innerJoin(Product as p).on(o.productId, p.id)
.leftJoin(Account as a).on(o.accountId, a.id)
.where.eq(o.productId, 123)
.orderBy(o.id).desc
.limit(4)
.offset(0)
}.map(Order(o, p, a)).list.apply()
上面的示例看起来与jOOQ代码非常相似,不同之处在于SELECT
DSL似乎比jOOQ的刚性更高。 例如,如何在该WHERE
子句中连接多个复杂谓词,或者根本不存在复杂谓词,现在还不是很明显。
然而,真正令人高兴的是它们利用Scala语言功能提供了一种非常流畅的动态SQL构造方法,如以下示例所示:
def findOrder(id: Long, accountRequired: Boolean) =
withSQL {
select
.from[Order](Order as o)
.innerJoin(Product as p).on(o.productId, p.id)
.map { sql =>
if (accountRequired)
sql.leftJoin(Account as a)
.on(o.accountId, a.id)
else
sql
}.where.eq(o.id, 13)
}.map { rs =>
if (accountRequired)
Order(o, p, a)(rs)
else
Order(o, p)(rs)
}.single.apply()
根据我们的理解,在SQL语句中间(在innerJoin
和where
之间)调用的map
方法可以使用lambda表达式转换中间DSL状态,该表达式允许在需要时附加leftJoin
。 显然,这也可以通过将中间DSL状态分配给局部变量来以更程序化的方式完成。
需要SQL查询DSL
过去,我们已经在博客中介绍了许多类似SQL查询DSL。 它们不断出现在各种API中的事实并非偶然。 SQL是一种类型安全且可组合的语言,很难通过基于字符串的API(例如JDBC,ODBC等)动态使用。
在Java或Scala等宿主语言中使用类型安全的内部特定于域的语言模型SQL带来了很大的优势。 但是,如果没有以完全可预见的方式精心设计DSL,则缺点可能很快就会显现出来。 以下面的ScalikeJDBC QueryDSL示例为例:
val ids = withSQL {
select(o.result.id).from(Order as o)
.where(sqls.toAndConditionOpt(
productId.map(id => sqls.eq(o.productId, id)),
accountId.map(id => sqls.eq(o.accountId, id))
))
.orderBy(o.id)
}.map(_.int(1)).list.apply()
这个toAndConditionOpt
方法确实是出乎意料的,并没有遵循最小惊讶原则 。
这就是jOOQ的API设计基于正式的BNF来模仿SQL本身的原因。 在此处阅读有关此内容的更多信息 。
翻译自: https://www.javacodegeeks.com/2014/03/a-sql-query-dsl-for-scala-by-scalikejdbc.html
scala dsl