scala dsl_ScalikeJDBC的Scala SQL查询DSL

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语句中间(在innerJoinwhere之间)调用的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

Scala中,可以通过使用Scala语言内置的JDBC或使用Scala提供的第三方库(如Slick)来执行SQL查询。以下是使用JDBC的示例代码: ```scala import java.sql.{Connection, DriverManager, ResultSet} val url = "jdbc:mysql://localhost:3306/mydatabase" val driver = "com.mysql.jdbc.Driver" val username = "myuser" val password = "mypassword" // register driver Class.forName(driver) // create connection val connection: Connection = DriverManager.getConnection(url, username, password) // create statement val statement = connection.createStatement() // execute query val query = "SELECT * FROM mytable" val resultSet: ResultSet = statement.executeQuery(query) // iterate through result set while (resultSet.next()) { val id = resultSet.getInt("id") val name = resultSet.getString("name") val age = resultSet.getInt("age") println(s"$id\t$name\t$age") } // close connection resultSet.close() statement.close() connection.close() ``` 在上面的示例中,我们使用JDBC驱动程序连接到MySQL数据库,并使用`Statement`对象执行查询。结果存储在`ResultSet`对象中,我们可以使用`next()`方法迭代结果集并获取每一行的值。 如果你使用的是Slick库,则可以使用其提供的DSL语言来执行SQL查询。以下是使用Slick的示例代码: ```scala import slick.jdbc.MySQLProfile.api._ val url = "jdbc:mysql://localhost:3306/mydatabase" val driver = "com.mysql.jdbc.Driver" val username = "myuser" val password = "mypassword" // create database connection val db = Database.forURL(url, username, password, driver) // define table schema case class MyTable(id: Int, name: String, age: Int) class MyTableTable(tag: Tag) extends Table[MyTable](tag, "mytable") { def id = column[Int]("id", O.PrimaryKey) def name = column[String]("name") def age = column[Int]("age") def * = (id, name, age) <> (MyTable.tupled, MyTable.unapply) } val myTableQuery = TableQuery[MyTableTable] // execute query val query = myTableQuery.result val result = db.run(query) // iterate through result set result.map { rows => rows.foreach { row => println(s"${row.id}\t${row.name}\t${row.age}") } } // close connection db.close() ``` 在上面的示例中,我们首先定义了表结构,并使用Slick的DSL语言定义了查询。然后,我们使用`db.run()`方法执行查询,并使用`map()`方法迭代结果集。由于Slick使用异步API,所以我们需要在最后关闭数据库连接。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值