Как выполнить список строковых операторов SQL для базы данных PostgreSQL в Scala, используя doobie? - PullRequest
3 голосов
/ 10 апреля 2019

Я портирую следующие 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 = {
  ???
}    

Я хочу выполнить (и напечатать)связка операторов SQL в одной транзакции с базой данных (предположим, что это Postgres) и все готово.

Как мне это сделать, используя Дуби ?

Примечание:

  1. Я не могу изменить интерфейс на мой execute()(в том числе я не могу добавить тип или неявные параметры). должен принимать имя пользователя String, пароль и т. Д., А также значение queries: String* и, таким образом, поддерживать интерфейс таким же, как у Python.

  2. Пожалуйста, укажите также всенеобходим импорт

Ответы [ 2 ]

3 голосов
/ 18 апреля 2019

Вы можете выполнить несколько запросов в одной транзакции в doobie, используя for-comppresion, например:

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 для jdbc и cats к вашему build.sbt. Doobie использует кошек под капотом, но я думаю, что явная зависимость может быть необходимой.

0 голосов
/ 18 апреля 2019

Попробуйте решить его только для одного запроса в транзакции и посмотрите, как выглядит эта сигнатура функции.

Затем посмотрите, как добраться оттуда до конечного пункта назначения.

...