【问题标题】:How to execute list of string SQL statements against a PostgreSQL db in Scala using doobie?如何使用 doobie 对 Scala 中的 PostgreSQL 数据库执行字符串 SQL 语句列表?
【发布时间】:2019-04-19 04:31:02
【问题描述】:

我将以下 10 行 Python 代码移植到 Scala:

import psycopg2

def execute(user, password, database, host, port, *queries):
    connection = psycopg2.connect(user=user, password=password, host=host, port=port, database=database)
    cursor = connection.cursor()
    for sql in queries:
        print(sql)
        cursor.execute(sql)
    connection.commit()
    cursor.close()
    connection.close()

我有以下等效的 Scala 代码:

def execute(user: String, password: String, database: String, host: String, port: Int, queries: String*): Unit = {
  ???
}    

我想在一个单个事务中针对数据库(假设它是 Postgres)执行(并打印)一堆 SQL 语句并完成。

我如何使用doobie 做到这一点?

注意:

  1. 我无法将接口更改为我的execute()(包括我无法添加类型或隐式参数)。它必须接受字符串用户、密码等和queries: String* 的可变参数,从而保持界面与 Python 界面相同。

  2. 还请提及所有需要的导入

【问题讨论】:

  • 这些查询是更新、选择还是插入?
  • 大多数情况下,创建表、创建视图、刷新、复制语句以加载数据、重命名等
  • 我检查了 doobie - 但我没有找到可能 - 所以你只对 doobie 的解决方案感兴趣 - 还是对一般的 scala 感兴趣?
  • doobie 很遗憾,因为这是我们在代码库中随处使用的库。我很惊讶它不可能在 Scala 中最流行的 ORM 库之一中执行 Seq[String]
  • 它是 ORM 库吗?

标签: scala doobie


【解决方案1】:

您可以使用 for-comprehension 在 doobie 的一个事务中运行多个查询,例如:

val query = for {
   _  <- sql"insert into person (name, age) values ($name, $age)".update.run
   id <- sql"select lastval()".query[Long].unique
} yield p

但此解决方案不适用于您的情况,因为您有一个动态的查询列表。幸运的是,我们可以使用来自猫的traverse

import cats.effect.ContextShift
import doobie._
import doobie.implicits._
import cats.effect._
import scala.concurrent.ExecutionContext
import cats.implicits._
import cats._
import cats.data._

def execute(user: String, password: String, database: String, host: String, port: Int, queries: String*): Unit = {

     //you can use other executor if you want
     //it would be better to pass context shift as implicit argument to method
    implicit val cs: ContextShift[IO] = IO.contextShift(ExecutionContext.global) 

    //let's create transactor  
    val xa = Transactor.fromDriverManager[IO](
      "org.postgresql.Driver",
       s"jdbc:postgresql://$host:$port/$database", //remember to change url or make it dynamic, if you run it agains another database
       user,
       password
    )

    val batch = queries
        .toList //we need to change String* to list, since String* doesn't have necessary typeclass for Aplicative
        .traverse(query => Update0(query, None).run) //we lift strings to Query0 and then run them, then we change List[ConnectionIO[Int]] to ConnectionIO[List[Int]]
        //above can be done in two steps using map and sequence

    batch  //now we've got single ConnectionIO which will run in one transaction
      .transact(xa) //let's make it IO[Int]
      .unsafeRunSync() //we need to block since your method returns Unit

  }

您的 IDE 可能会显示此代码无效,但它是正确的。 IDE 无法处理 Scala 魔法。

您也可以考虑使用unsafeRunTimed 而不是unsafeRunSync 来添加时间限制。

另外,请记住将postgresql driver for jdbccats 添加到您的build.sbt。 Doobie 在后台使用猫,但我认为显式依赖可能是必要的。

【讨论】:

    【解决方案2】:

    尝试仅解决事务中的一个查询并查看该函数签名的样子。

    然后看看如何从那里到达您的最终目的地。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-12-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多