如何使用 doobie 在 Scala 中针对 PostgreSQL 数据库执行字符串列表 SQL 语句?
How to execute list of string SQL statements against a PostgreSQL db in Scala using doobie?
我正在将以下 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 做到这一点?
注:
我无法将接口更改为我的 execute()
(包括我无法添加类型或隐式参数)。它 必须 接受字符串用户、密码等和 queries: String*
的可变参数,从而使界面与 Python 相同。
还请提及所有需要的进口商品
尝试仅针对交易中的一个查询解决它,并查看该函数签名的样子。
然后看看如何从那里到达你的最终目的地。
您可以 运行 在 doobie 中使用 for-comprehension 在一个事务中进行多个查询,例如:
val query = for {
_ <- sql"insert into person (name, age) values ($name, $age)".update.run
id <- sql"select lastval()".query[Long].unique
} yield p
但此解决方案不适用于您的情况,因为您有一个动态的查询列表。幸运的是,我们可以使用来自 cats 的 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 会告诉您此代码无效,但它是正确的。 IDEs 无法处理 Scala 魔法。
您也可以考虑使用 unsafeRunTimed
而不是 unsafeRunSync
来添加时间限制。
此外,记得将 postgresql driver for jdbc and cats 添加到您的 build.sbt
。 Doobie 在后台使用猫,但我认为可能需要显式依赖。
我正在将以下 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 做到这一点?
注:
我无法将接口更改为我的
execute()
(包括我无法添加类型或隐式参数)。它 必须 接受字符串用户、密码等和queries: String*
的可变参数,从而使界面与 Python 相同。还请提及所有需要的进口商品
尝试仅针对交易中的一个查询解决它,并查看该函数签名的样子。
然后看看如何从那里到达你的最终目的地。
您可以 运行 在 doobie 中使用 for-comprehension 在一个事务中进行多个查询,例如:
val query = for {
_ <- sql"insert into person (name, age) values ($name, $age)".update.run
id <- sql"select lastval()".query[Long].unique
} yield p
但此解决方案不适用于您的情况,因为您有一个动态的查询列表。幸运的是,我们可以使用来自 cats 的 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 会告诉您此代码无效,但它是正确的。 IDEs 无法处理 Scala 魔法。
您也可以考虑使用 unsafeRunTimed
而不是 unsafeRunSync
来添加时间限制。
此外,记得将 postgresql driver for jdbc and cats 添加到您的 build.sbt
。 Doobie 在后台使用猫,但我认为可能需要显式依赖。