使用 Groovy 和 GraalVM 操作 SQL 数据库

作者:Paul King
发布时间:2022-07-29 02:07PM


本周,有一段有趣的 视频 和一篇 博客文章 讨论了最新的 GraalVM 22.2 版本。该版本包含众多新功能和改进,包括

  • 更小的原生可执行文件

  • 能够在原生可执行文件中生成堆转储

  • IntelliJ IDEA 中的实验性原生镜像调试

  • 能够将软件物料清单 (SBOM) 嵌入可执行文件中,以增强安全性(使用 GraalVM 企业版时)

  • 原生元数据集成

这篇博客介绍了其中的最后一个功能。我们将使用视频中讨论的 H2 数据库 作为运行示例。

原生元数据

任何使用过 GraalVM 的人都知道,通常需要向原生编译器提供某些信息。某些类可以在构建时初始化,而另一些应该在运行时初始化。如果访问某些类型的资源,则必须向编译器提供有关这些资源的知识。应用程序中可能通过反射调用或涉及序列化的部分,可能被认为不可达,因此不会被编译器自动包含。

每个应用程序中使用的库都将有自己的类和资源集,这些类和资源通常需要由使用该库的任何人处理。原生元数据存储库在每个库的基础上保存此信息的共享副本。一旦有人填充了元数据,其他使用相同库的项目就可以自动获取该信息。我们将在稍后详细介绍元数据集成,但首先,让我们看看我们的数据库应用程序。

在 Groovy 中使用 SQL

应用程序创建并填充了一个名为 customer 的数据库,其中包含四个客户。然后打印出它们。

import groovy.sql.Sql
import groovy.transform.CompileStatic

@CompileStatic
class H2Demo {
    static void main(args) {
        Sql.withInstance('jdbc:h2:./data/test') { sql ->
            sql.execute 'DROP TABLE IF EXISTS customers'
            sql.execute 'CREATE TABLE customers(id INTEGER AUTO_INCREMENT, name VARCHAR)'
            for (cust in ['Lord Archimonde', 'Arthur', 'Gilbert', 'Grug']) {
                sql.executeInsert "INSERT INTO customers(name) VALUES $cust"
            }
            println sql.rows('SELECT * FROM customers').join('\n')
        }
    }
}

Groovy 的 Sql 类使此操作变得相对容易。withInstance 方法将创建数据库连接,并在完成后关闭连接。executeInsert 方法使用的是 Groovy 插值字符串 (GString),它在后台创建了一个准备好的语句。

配置我们的原生构建

以下是我们的构建文件

plugins {
    id 'application'
    id 'groovy'
    id 'org.graalvm.buildtools.native'
}

application {
    mainClass = 'H2Demo'
}

repositories {
    mavenCentral()
}

dependencies {
   implementation 'com.h2database:h2:2.1.210'
   implementation 'org.apache.groovy:groovy:4.0.4'
   implementation 'org.apache.groovy:groovy-sql:4.0.4'
}

graalvmNative {
    agent {
        defaultMode = 'standard'
    }
    metadataRepository {
        enabled = true
    }
    binaries {
        main {
            buildArgs.addAll(
//                    '-H:IncludeSBOM=cyclonedx',
                    '--report-unsupported-elements-at-runtime',
                    '--initialize-at-run-time=groovy.grape.GrapeIvy,org.h2.store.fs.niomem.FileNioMemData',
                    '--initialize-at-build-time',
                    '--no-fallback',
            )
        }
    }
}

我们使用 graalvm 原生构建插件。我们定义了 Groovy 和 H2 的依赖项。我们还可以向原生编译器提供任何必要的参数。重要的是,我们启用了与元数据存储库的集成。

当我们运行构建时,它将自动为我们创建原生应用程序。

paulk@pop-os:/extra/projects/groovy-graalvm-h2$ ./gradlew clean nativeRun
...
> Task :nativeCompile
[native-image-plugin] Using executable path: /extra/devtools/graalvm-ce-java17-22.2.0/bin/native-image
==========================================================================================
GraalVM Native Image: Generating 'H2Demo' (executable)...
==========================================================================================
...
[1/7] Initializing...                                                                                    (5.3s @ 0.26GB)
 Version info: 'GraalVM 22.2.0 Java 17 CE'
 Java version info: '17.0.4+8-jvmci-22.2-b06'
 C compiler: gcc (linux, x86_64, 11.2.0)
 Garbage collector: Serial GC
 1 user-specific feature(s)
 - com.oracle.svm.polyglot.groovy.GroovyIndyInterfaceFeature
[2/7] Performing analysis...  [************]                                                            (51.7s @ 1.82GB)
  10,597 (90.60%) of 11,697 classes reachable
  17,002 (64.13%) of 26,510 fields reachable
  58,165 (63.45%) of 91,666 methods reachable
     393 classes,   100 fields, and 2,057 methods registered for reflection
      65 classes,    74 fields, and    55 methods registered for JNI access
       4 native libraries: dl, pthread, rt, z
[3/7] Building universe...                                                                               (8.0s @ 4.02GB)
[4/7] Parsing methods...      [**]                                                                       (4.8s @ 3.85GB)
[5/7] Inlining methods...     [***]                                                                      (3.0s @ 1.72GB)
[6/7] Compiling methods...    [******]                                                                  (38.0s @ 3.63GB)
[7/7] Creating image...                                                                                  (5.9s @ 1.70GB)
  26.65MB (46.64%) for code area:    38,890 compilation units
  28.04MB (49.05%) for image heap:  359,812 objects and 66 resources
   2.46MB ( 4.31%) for other data
  57.15MB in total
------------------------------------------------------------------------------------------
Top 10 packages in code area:                               Top 10 object types in image heap:
   1.48MB sun.security.ssl                                     5.85MB byte[] for code metadata
   1.06MB java.util                                            2.82MB java.lang.String
 979.43KB java.lang.invoke                                     2.78MB java.lang.Class
 758.29KB org.apache.groovy.parser.antlr4                      2.47MB byte[] for general heap data
 723.92KB com.sun.crypto.provider                              2.04MB byte[] for java.lang.String
 588.57KB org.h2.table                                       910.68KB com.oracle.svm.core.hub.DynamicHubCompanion
 582.06KB org.h2.command                                     764.95KB java.util.HashMap$Node
 494.23KB org.codehaus.groovy.classgen                       761.53KB java.lang.Object[]
 476.03KB c.s.org.apache.xerces.internal.impl.xs.traversers  715.65KB byte[] for embedded resources
 468.69KB java.lang                                          584.75KB java.util.HashMap$Node[]
  18.87MB for 370 more packages                                8.28MB for 2535 more object types
------------------------------------------------------------------------------------------
                        3.9s (3.2% of total time) in 30 GCs | Peak RSS: 6.22GB | CPU load: 6.48
------------------------------------------------------------------------------------------
Produced artifacts:
 /extra/projects/groovy-graalvm-h2/build/native/nativeCompile/H2Demo (executable)
 /extra/projects/groovy-graalvm-h2/build/native/nativeCompile/H2Demo.build_artifacts.txt (txt)
===========================================================================================
Finished generating 'H2Demo' in 2m 1s.
    [native-image-plugin] Native Image written to: /extra/projects/groovy-graalvm-h2/build/native/nativeCompile

> Task :nativeRun
[ID:1, NAME:Lord Archimonde]
[ID:2, NAME:Arthur]
[ID:3, NAME:Gilbert]
[ID:4, NAME:Grug]

检查原生镜像速度

我们还可以检查原生镜像构建后的速度

paulk@pop-os:/extra/projects/groovy-graalvm-h2$ time build/native/nativeCompile/H2Demo
[ID:1, NAME:Lord Archimonde]
[ID:2, NAME:Arthur]
[ID:3, NAME:Gilbert]
[ID:4, NAME:Grug]

real	0m0.027s
user	0m0.010s
sys	0m0.011s

更多信息

查看存储库中的完整源代码
https://github.com/paulk-asert/groovy-graalvm-h2

结论

我们已经查看了一个简单的 H2 数据库应用程序以及使用 Groovy 和 GraalVM 创建原生应用程序所涉及的步骤。