运行生成的 jar 文件时,如何使 Apache Derby 数据库正常工作?

问题描述

我创建了一个 JavaFX 应用程序,并生成一个将启动的 Jar 文件(使用 Gradle 生成)。

当应用程序启动时,它不会连接到嵌入式数据库。我觉得好像我错过了一些非常简单的东西,但经过大量研究,我一直无法弄清楚。从命令提示符运行 jar 文件时,出现以下错误java.lang.classNotFoundException: org.apache.derby.jdbc.EmbeddedDriver

从我所做的阅读中,我知道我可以将它添加到我的类路径中,但经过多次尝试后我没有成功,我已经让这个应用程序在另一台计算机上运行。这是可能的,对吧?

如果可能的话,我很想更改我的 build.gradle 文件中的某些内容,或者将 .jar 包含在一个文件夹中或类似的东西中,这将使运行程序的人变得简单。程序大小不是几个 GB 的问题。

解决方法

你有两个问题

  • 驱动程序类不在您的应用程序 jar 中
  • 如果您将该数据库嵌入到应用程序 jar 中,您将无法对其进行写入

你可以制作一个“胖罐子”,但这并不能解决第二个问题。您确实需要使 an installer 执行此操作,以便将 db 保留在文件系统中,以便将其写入。

,

问题

Gradle 的部分功能是依赖管理。这意味着它知道您需要哪些依赖项以及如何找到它们(基于构建脚本中的配置)。当您通过 Gradle 执行/构建应用程序时,该工具将自动搜索存储库、下载+缓存依赖项,并将这些依赖项放在类路径/模块路径上。部署 JAR 文件后,Gradle 不再涉及,因此您的部署负责包含所需的依赖项。

换句话说,您需要将应用程序的依赖项与应用程序 JAR 文件一起发送。


解决方案

您基本上只需要确保将应用程序的依赖项包含在您的应用程序中。这里至少有三种方法可以做到这一点。

复制依赖

作为构建过程的一部分,将依赖项复制到构建文件夹中。这是此类任务的示例,使用 Kotlin DSL

tasks {
    val jar by existing(Jar::class)

    val copyDependencies by registering(Copy::class) {
        from(configurations.runtimeClasspath)
        into(jar.get().destinationDirectory)
    }

    jar.configure {
        finalizedBy(copyDependencies)
    }
}

现在,如果您执行 ./gradlew jar,Gradle 将创建 JAR 文件,然后将依赖项复制到与 JAR 文件相同的目录中。然后你只需要确保所有的 JAR 文件都部署在一起。

如果我没记错的话,默认的类路径是工作目录。但是要指定类路径,您将在执行应用程序时使用 -cp-classpath--class-path。如果需要,模块路径用 -p--module-path 设置。

胖罐

创建所谓的“fat”或“uber”JAR 文件。这是一个 JAR 文件,不仅包含您自己的应用程序代码,还包含您应用程序的所有依赖项。您可以为此配置 jar 任务,但简单地应用 Gradle Shadow Plugin 可能会更容易。

// Kotlin DSL
plugins {
    id("com.github.johnrengelman.shadow") version "<version>"
    // other plugins...
}

然后您将使用 ./gradlew shadowJar 创建胖 JAR。有关详细信息,请参阅用户指南。

自包含应用

使用 jpackage 之类的工具创建自包含的可执行文件。该工具为您提供了一个应用程序,其中嵌入了所有代码和 JRE,然后为您提供了一个安装程序或本机可执行文件(例如,Windows 上的 exe)。这是 jpackageuser guide。 Gradle 插件可以让您更轻松地使用 Gradle 中的 jpackage,例如 The Badass JLink Plugin

注意 jpackage 是在 Java 14 中添加的,并且一直孵化到 Java 16。还要注意 jpackage 不能“跨包”。也就是说,如果您在 Windows 上构建应用程序,那么您只能为 Windows 创建安装程序/可执行文件; MacOS 和 Linux 也一样。如果您需要为多个平台打包,那么您需要访问每个平台。


JavaFX

既然你用 JavaFX 标记了这个问题,我想提醒一下。不过,如果您不使用 JavaFX 9+,那么这与您无关。

从技术上讲,JavaFX 仅支持作为命名模块加载。这意味着它需要通过 --module-path 或通过将其包含在由 jlink / jpackage 构建的自定义运行时映像中来放置在模块路径上。从 JavaFX 16 开始,如果从 未命名 模块(即类路径)加载 JavaFX,则会发出警告。

可执行 JAR 文件放置在类路径中。这包括胖 JAR。如果您没有使用包含 JavaFX 的 JDK——这意味着您在 JavaFX 依赖项中有 Gradle 拉取——那么 JavaFX 将包含在您的胖 JAR 中并放置在类路径中。现在,尽管不受支持并且现在发出警告,但如果 JavaFX 在类路径上,目前似乎没有任何问题。除了一个警告:您的主类不能是 javafx.application.Application 的子类。您必须创建一个单独的主类来简单地启动 JavaFX。

因此,我强烈建议使用 jpackage 来部署 JavaFX 应用程序。您可能还想阅读this Q&A