预测试 SBT 任务:无法实例化 JDBC 驱动程序

Pre-test SBT task: Unable to instantiate JDBC driver

我在使用 Flyway 进行 SBT 任务 到 运行 迁移时遇到问题;当我 运行 任务时出现异常。有什么办法可以解决吗?

org.flywaydb.core.api.FlywayException: Unable to instantiate JDBC driver: org.postgresql.Driver => Check whether the jar file is present

以下代码有效,当我在 BeforeAll 中 运行 时,在我的 tests (ScalaTest) 中,但在我移动它时不起作用进入 SBT 任务。

val flyway = Flyway
  .configure()
  .locations("filesystem:./**/resources/db/migrations/")
  .dataSource("jdbc:postgresql://localhost:5432/my_database", "my_user", "secret")
  .load()
      
flyway.clean()
flyway.migrate()

我的 /build.sbt 文件如下所示:

import org.flywaydb.core.Flyway

lazy val migrate = taskKey[Unit]("Migrate database")

lazy val migrateTask = Def.task {
  println("Migrate")
  val flyway = Flyway
    .configure()
    .locations("filesystem:./**/resources/db/migrations/")
    .dataSource("jdbc:postgresql://localhost:5432/my_database", "my_user", "secret")
    .load()

  flyway.clean()
  flyway.migrate()
}

val IntegrationTest = config("integration") extend Test

lazy val integrationTestSettings = inConfig(IntegrationTest)(Defaults.testSettings) ++ List(
  IntegrationTest / fork := false,
  IntegrationTest / parallelExecution := false,
  IntegrationTest / sourceDirectory := baseDirectory.value / "src/test/integration",
  IntegrationTest / test := {
    (IntegrationTest / test) dependsOn migrateTask
  }.value
)

lazy val root = Project(id = "hello", base = file("."))
  .configs(Configs.all: _*)
  .settings(
    integrationTestSettings,
    libraryDependencies += "org.scalatest" %% "scalatest" % "3.1.4",
  )

我的 /project/build.sbt 看起来像这样:

libraryDependencies ++= List(
  "org.flywaydb"    % "flyway-core" % "7.6.0",
  "org.postgresql"  % "postgresql"  % "42.2.19",
)

我使用的版本是:

有人知道我为什么会收到这个错误吗?我该如何解决?

如有任何帮助,我们将不胜感激。谢谢:)

在 Flyway 存储库上搜索,错误消息来自此处 - https://github.com/flyway/flyway/blob/9033185ab8bfa56b0dae9136c04763cdccc50081/flyway-core/src/main/java/org/flywaydb/core/internal/jdbc/DriverDataSource.java#L165-L182 它正在尝试从类加载器加载数据库驱动程序。这些 ClassLoader 技术有时会与 sbt 将分层 ClassLoader 设置为 运行 sbt 本身发生冲突。这是我对正在发生的事情的猜测。

我们如何解决这个问题?

您说过 运行将其作为测试工作的一部分,因此也许您可以为此目的创建一个子项目?

ThisBuild / scalaVersion := "2.13.4"
lazy val migrate = taskKey[Unit]("Migrate database")

lazy val root = (project in file("."))
  .settings(
    name := "hello",
    migrate := (migrateProj / run).toTask("").value
  )

// utility project to run database migration
lazy val migrateProj = (project in file("migrate"))
  .settings(
    libraryDependencies ++= List(
      "org.flywaydb"    % "flyway-core" % "7.6.0",
      "org.postgresql"  % "postgresql"  % "42.2.19",
    ),
    Compile / run / fork := true,
    publish / skip := true,
  )

migrate/Migrate.scala

object Migrate extends App {
  println("migrate")

  // rest of the code here...
}

现在您可以运行

sbt:flyway> migrate
[info] running (fork) Migrate
[info] migrate
[success] Total time: 4 s, completed Mar 6, 2021 9:03:07 PM

关于分层类加载器的详细信息

ClassLoader 技术有时会与 sbt 将分层 ClassLoader 设置为 运行 sbt 本身发生冲突。 sbt-the-Bash-脚本允许用户使用 project/build.properties 选择 sbt 版本,使用 build.sbt 选择 Scala 版本。这两者都使 sbt build 具有声明性和可重复性,通常是一件好事。但是,使用 Scala 2.10 编写的 sbt 启动器如何启动使用 Scala 2.12 编写的 sbt 1.4.x,然后启动您的 Scala 2.13 应用程序?这些边界的每一个交叉都是通过创建一个分层的ClassLoader来完成的,就像电影盗梦空间一样。