在 Quarkus 中使用纯 Java 运行 SQLite

如果我们可以在纯 Java 中运行 C 语言数据库,零配置,甚至轻松将其编译为原生镜像,那会怎么样?有了新的 Quarkiverse 扩展 quarkus-jdbc-sqlite4j,您就可以做到这一点。

传统上,Java 中的嵌入式数据库需要重新实现其 C 语言对应版本,这通常会导致行为差异、优化缺失和错误修复延迟。然而,sqlite4j 提供了一个 JDBC 驱动程序,该驱动程序利用了原始 SQLite C 语言实现,同时安全地运行在一个沙箱环境中。

实践示例

要查看 quarkus-jdbc-sqlite4j 的实际应用,您可以从任何现有的 Quarkus 应用程序或 quickstarts 开始。如果您需要现成的示例,请查看 此演示仓库,它使用 Hibernate ORM 将 SQLite 集成到 Quarkus 应用程序中。

只需更改 JDBC 驱动程序的依赖项,您就可以在应用程序中嵌入功能齐全的 SQLite 数据库,同时保留原生 SQLite 实现的所有优势。

要开始,请将扩展依赖添加到您的 pom.xml 文件中

<dependency>
    <groupId>io.quarkiverse.jdbc</groupId>
    <artifactId>quarkus-jdbc-sqlite4j</artifactId>
</dependency>

然后,使用标准的 JDBC 设置配置您的 Quarkus 应用程序以使用 SQLite

quarkus.datasource.db-kind=sqlite
quarkus.datasource.jdbc.url=jdbc:sqlite:sample.db
quarkus.datasource.jdbc.min-size=1

您现在可以像往常一样使用 Hibernate 和 Panache 来使用您的数据源。请注意,我们将最小连接池大小保持 > 0,以避免将数据库从磁盘重复复制到内存中。

在安全沙箱中运行

在底层,SQLite 在完全内存中的沙箱环境中运行,确保安全性和隔离性。

Virtual FileSystem Usage

当打开到本地文件的连接时,会发生以下情况:

  1. 数据库文件从磁盘复制到内存中的虚拟文件系统。

  2. 建立到内存数据库的连接。

虽然这种方法非常安全,但许多用户需要持久化数据库更改。一个推荐的解决方案是定期将内存数据库备份到磁盘。这可以通过一个计划任务来实现,该任务:

  1. 将内存数据库备份到新文件中。

  2. 将备份文件复制到主机文件系统。

  3. 原子地用新备份替换旧数据库文件。

这种设置确保了无缝的体验,同时保持了 SQLite 的沙箱安全性。您可以根据自己的具体需求调整此方法。

以下是一个示例实现

@ApplicationScoped
public class SQLiteBackup {
    @ConfigProperty(name = "quarkus.datasource.jdbc.url")
    String jdbcUrl;

    @Inject
    AgroalDataSource dataSource;

    // Execute a backup every 10 seconds
    @Scheduled(delay=10, delayUnit=TimeUnit.SECONDS, every="10s")
    void scheduled() { backup(); }

    // Execute a backup during shutdown
    public void onShutdown(@Observes ShutdownEvent event) { backup(); }

    void backup() {
        String dbFile = jdbcUrl.substring("jdbc:sqlite:".length());

        var originalDbFilePath = Paths.get(dbFile);
        var backupDbFilePath = originalDbFilePath
                                    .toAbsolutePath()
                                    .getParent()
                                    .resolve(originalDbFilePath.getFileName() + "_backup");

        try (var conn = dataSource.getConnection();
                var stmt = conn.createStatement()) {
            // Execute the backup
            stmt.executeUpdate("backup to " + backupDbFilePath);
            // Atomically replace the DB file with its backup
            Files.move(backupDbFilePath, originalDbFilePath,
                StandardCopyOption.ATOMIC_MOVE,
                StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException | SQLException e) {
            throw new RuntimeException("Failed to back up the database", e);
        }
    }
}

技术深入探讨

sqlite4j 将官方 SQLite C 语言 amalgamation 编译为 WebAssembly (Wasm),然后使用 Chicory AOT 编译器将其转换为 Java 字节码。这使得 SQLite 可以在纯 Java 环境中运行,同时保持其全部功能。

SQLite compilation

安全性和隔离性

这种方法的一个关键优势是安全性。在 Wasm 沙箱中运行 SQLite 可确保内存安全并将其与主机系统隔离,使其成为需要嵌入式数据库但又避免原生代码执行风险的应用程序的绝佳选择。

结论

有了新的 quarkus-jdbc-sqlite4j 扩展,您可以兼顾两全其美:SQLite 的强大功能和可靠性,以及 Java 的安全性和可移植性。此扩展无缝地将 SQLite 集成到 Quarkus 应用程序中,同时保持轻量级和安全架构。最重要的是,所有这些都可以轻松地与 native-image 编译。

准备好尝试了吗?在您的项目中试用 quarkus-jdbc-sqlite4j,体验在 Quarkus 中使用纯 Java 运行 SQLite 的优势!