- Micronaut Test
- Testcontainers
- GraalVM
Description
The example exposes some REST endpoints and stores data in a MySQL database using Micronaut Data R2DBC.
Based on: https://guides.micronaut.io/latest/micronaut-data-r2dbc-repository-gradle-java.html

Requirements
- JDK 17+
- Docker installed to run MySQL and to run tests using Testcontainers.
Project Structure

Running & Testing Local
#export DATASOURCES_DEFAULT_URL=jdbc:mysql://localhost:3306/db
#export DATASOURCES_DEFAULT_USERNAME=root
#export DATASOURCES_DEFAULT_PASSWORD=4kDGQDYe4JxDjRd
#
#export R2DBC_DATASOURCES_DEFAULT_URL=r2dbc:mysql://localhost:3306/db
#export R2DBC_DATASOURCES_DEFAULT_USERNAME=root
#export R2DBC_DATASOURCES_DEFAULT_PASSWORD=4kDGQDYe4JxDjRdTesting
./gradlew test
[test-resources-service] 18:45:12.878 [main] INFO i.m.c.DefaultApplicationContext$RuntimeConfiguredEnvironment - Established active environments: [test]
[test-resources-service] 18:45:12.886 [ForkJoinPool.commonPool-worker-4] INFO i.m.t.e.TestResourcesResolverLoader - Loaded 3 test resources resolvers: io.micronaut.testresources.mysql.MySQLTestResourceProvider, io.micronaut.testresources.r2dbc.mysql.R2DBCMySQLTestResourceProvider, io.micronaut.testresources.testcontainers.GenericTestContainerProvider
[test-resources-service] 18:45:13.092 [pool-1-thread-1] INFO o.t.d.DockerClientProviderStrategy - Loaded org.testcontainers.dockerclient.UnixSocketClientProviderStrategy from ~/.testcontainers.properties, will try it first
[test-resources-service] 18:45:13.220 [pool-1-thread-1] INFO o.t.d.DockerClientProviderStrategy - Found Docker environment with local Unix socket (unix:///var/run/docker.sock):internalStartTestResourcesService
[test-resources-service] 18:45:13.221 [pool-1-thread-1] INFO o.testcontainers.DockerClientFactory - Docker host IP address is localhost
[test-resources-service] 18:45:13.238 [pool-1-thread-1] INFO o.testcontainers.DockerClientFactory - Connected to docker:
Server Version: 25.0.4urcesService
API Version: 1.44
Operating System: Linux Mint 21.1
Total Memory: 15815 MB
[test-resources-service] 18:45:13.244 [pool-1-thread-1] INFO o.testcontainers.images.PullPolicy - Image pull policy will be performed by: DefaultPullPolicy()
[test-resources-service] 18:45:13.245 [pool-1-thread-1] INFO o.t.utility.ImageNameSubstitutor - Image name substitution will be performed by: DefaultImageNameSubstitutor (composite of 'ConfigurationFileImageNameSubstitutor' and 'PrefixingImageNameSubstitutor')
[test-resources-service] 18:45:13.270 [pool-1-thread-1] INFO tc.testcontainers/ryuk:0.6.0 - Creating container for image: testcontainers/ryuk:0.6.0
[test-resources-service] 18:45:13.301 [pool-1-thread-1] INFO o.t.utility.RegistryAuthLocator - Credential helper/store (docker-credential-desktop) does not have credentials for https://index.docker.io/v1/
[test-resources-service] 18:45:13.568 [pool-1-thread-1] INFO tc.testcontainers/ryuk:0.6.0 - Container testcontainers/ryuk:0.6.0 is starting: d34402cccc5dda38e0366779edb837520f12a1de1d0f60f8fd7566b08540a030vice
[test-resources-service] 18:45:13.930 [pool-1-thread-1] INFO tc.testcontainers/ryuk:0.6.0 - Container testcontainers/ryuk:0.6.0 started in PT0.659541515S
[test-resources-service] 18:45:13.934 [pool-1-thread-1] INFO o.t.utility.RyukResourceReaper - Ryuk started - will monitor and terminate Testcontainers containers on JVM exit
[test-resources-service] 18:45:13.934 [pool-1-thread-1] INFO o.testcontainers.DockerClientFactory - Checking the system...
[test-resources-service] 18:45:13.934 [pool-1-thread-1] INFO o.testcontainers.DockerClientFactory - ✔︎ Docker server version should be at least 1.6.0
[test-resources-service] 18:45:14.083 [scheduled-executor-thread-1] INFO i.m.t.server.ExpiryManager - Test resources server will automatically be shutdown if it doesn't receive requests for 60 minutesesService
[test-resources-service] 18:45:14.112 [main] INFO i.m.t.server.TestResourcesService - A Micronaut Test Resources server is listening on port 37551, started in 1345ms
[test-resources-service] 18:45:16.155 [virtual-executor7] INFO i.m.t.testcontainers.TestContainers - Starting test container mysql
[test-resources-service] 18:45:16.163 [virtual-executor7] INFO tc.mysql:latest - Creating container for image: mysql:latest
[test-resources-service] 18:45:16.238 [virtual-executor7] INFO tc.mysql:latest - Container mysql:latest is starting: fa68be62bcc77e283b1d88bd0749a368c6b44d1e73d8c256589deec3019b582e> 0 tests completed
[test-resources-service] 18:45:16.542 [virtual-executor7] INFO tc.mysql:latest - Waiting for database connection to become available at jdbc:mysql://localhost:32771/test using query 'SELECT 1'ompleted
[test-resources-service] 18:45:35.009 [virtual-executor7] INFO tc.mysql:latest - Container mysql:latest started in PT18.84621062S
[test-resources-service] 18:45:35.009 [virtual-executor7] INFO tc.mysql:latest - Container is started (JDBC URL: jdbc:mysql://localhost:32771/test)
micronaut-data-r2dbc-repository-gradle-java/build/reports/tests/test/index.html

Running
./gradlew run
curl -X "POST" "http://localhost:8080/genres" \
-H 'Content-Type: application/json; charset=utf-8' \
-d $'{ "name": "music" }'
Running Native
Setting Docker MySQL
service mysql stop
docker-compose.yaml
version: '3.1'
services:
db_mysql:
container_name: db_mysql
image: mysql
ports:
- "3306:3306"
restart: always
environment:
MYSQL_USER: admin
MYSQL_PASSWORD: rNZzq5U37DqJlNe
MYSQL_ROOT_PASSWORD: 4kDGQDYe4JxDjRd
volumes:
- /var/lib/mysqld:/var/lib/mysqlFlyway environment variables:
export DATASOURCES_DEFAULT_URL=jdbc:mysql://localhost:3306/db
export DATASOURCES_DEFAULT_USERNAME=root
export DATASOURCES_DEFAULT_PASSWORD=4kDGQDYe4JxDjRdR2DBC environment variables:
export R2DBC_DATASOURCES_DEFAULT_URL=r2dbc:mysql://localhost:3306/db
export R2DBC_DATASOURCES_DEFAULT_USERNAME=root
export R2DBC_DATASOURCES_DEFAULT_PASSWORD=4kDGQDYe4JxDjRdNative executable generation
Customize the name of the native executable
build.gradle
graalvmNative {
binaries {
main {
imageName.set('mn-graalvm-application')
buildArgs.add('--verbose')
}
}
}./gradlew nativeCompile
========================================================================================================================
GraalVM Native Image: Generating 'mn-graalvm-application' (executable)...
========================================================================================================================
For detailed information and explanations on the build output, visit:
https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/BuildOutput.md
------------------------------------------------------------------------------------------------------------------------
[1/8] Initializing... (7.0s @ 0.14GB)
Java version: 21.0.1+12, vendor version: Oracle GraalVM 21.0.1+12.1
Graal compiler: optimization level: 2, target machine: x86-64-v3, PGO: ML-inferred
C compiler: gcc (linux, x86_64, 11.4.0)
Garbage collector: Serial GC (max heap size: 80% of RAM)
4 user-specific feature(s):
- com.oracle.svm.thirdparty.gson.GsonFeature
- io.micronaut.core.io.service.ServiceLoaderFeature
- io.micronaut.flyway.StaticResourceFeature
- io.micronaut.jackson.JacksonDatabindFeature
------------------------------------------------------------------------------------------------------------------------
Build resources:
- 11.67GB of memory (75.6% of 15.45GB system memory, determined at start)
- 8 thread(s) (100.0% of 8 available processor(s), determined at start)
[2/8] Performing analysis... [*****] (83.5s @ 1.92GB)
20,973 reachable types (91.0% of 23,041 total)
30,782 reachable fields (59.8% of 51,492 total)
113,092 reachable methods (65.0% of 173,971 total)
6,880 types, 671 fields, and 6,704 methods registered for reflection
89 types, 86 fields, and 67 methods registered for JNI access
4 native libraries: dl, pthread, rt, z
[3/8] Building universe... (12.2s @ 2.11GB)
[4/8] Parsing methods... [*****] (28.5s @ 2.88GB)
[5/8] Inlining methods... [***] (3.3s @ 1.99GB)
[6/8] Compiling methods... [*************] (188.0s @ 1.90GB)
[7/8] Layouting methods... [****] (16.1s @ 2.27GB)
[8/8] Creating image... [***] (11.1s @ 2.49GB)
56.57MB (53.13%) for code area: 67,644 compilation units
41.82MB (39.28%) for image heap: 472,651 objects and 767 resources
8.08MB ( 7.59%) for other data
106.47MB in total
------------------------------------------------------------------------------------------------------------------------
Top 10 origins of code area: Top 10 object types in image heap:
15.34MB java.base 15.98MB byte[] for code metadata
5.03MB svm.jar (Native Image) 6.87MB byte[] for java.lang.String
4.06MB java.xml 5.11MB java.lang.Class
2.90MB mysql-connector-j-8.3.0.jar 3.32MB java.lang.String
2.46MB jackson-databind-2.16.2.jar 1.33MB byte[] for reflection metadata
1.42MB netty-buffer-4.1.108.Final.jar 1.13MB byte[] for embedded resources
1.33MB reactor-core-3.6.1.jar 983.11kB com.oracle.svm.core.hub.DynamicHubCompanion
1.26MB micronaut-inject-4.3.12.jar 799.15kB byte[] for general heap data
1.09MB micronaut-http-server-netty-4.3.12.jar 609.78kB c.o.svm.core.hub.DynamicHub$ReflectionMetadata
1.02MB flyway-core-10.6.0.jar 455.34kB java.lang.String[]
20.20MB for 86 more packages 5.30MB for 4033 more object types
Use '-H:+BuildReport' to create a report with more details.
------------------------------------------------------------------------------------------------------------------------
Security report:
- Binary includes Java deserialization.
- Use '--enable-sbom' to embed a Software Bill of Materials (SBOM) in the binary.
------------------------------------------------------------------------------------------------------------------------
Recommendations:
G1GC: Use the G1 GC ('--gc=G1') for improved latency and throughput.
PGO: Use Profile-Guided Optimizations ('--pgo') for improved throughput.
INIT: Adopt '--strict-image-heap' to prepare for the next GraalVM release.
HEAP: Set max heap for improved and more predictable memory usage.
CPU: Enable more CPU features with '-march=native' for improved performance.
------------------------------------------------------------------------------------------------------------------------
32.0s (9.1% of total time) in 397 GCs | Peak RSS: 4.96GB | CPU load: 6.22
------------------------------------------------------------------------------------------------------------------------
Produced artifacts:
/home/zbyszek/IdeaProjects/reactive/micronaut/tmp-micronaut-r2dbc-mysql-maven/micronaut-data-r2dbc-repository-gradle-java/build/native/nativeCompile/mn-graalvm-application (executable)
========================================================================================================================
Finished generating 'mn-graalvm-application' in 5m 51s.
[native-image-plugin] Native Image written to: /home/zbyszek/IdeaProjects/reactive/micronaut/tmp-micronaut-r2dbc-mysql-maven/micronaut-data-r2dbc-repository-gradle-java/build/native/nativeCompile
BUILD SUCCESSFUL in 5m 56s
micronaut-data-r2dbc-repository-gradle-java/build/native/nativeCompile
./mn-graalvm-application
__ __ _ _
| \/ (_) ___ _ __ ___ _ __ __ _ _ _| |_
| |\/| | |/ __| '__/ _ \| '_ \ / _` | | | | __|
| | | | | (__| | | (_) | | | | (_| | |_| | |_
|_| |_|_|\___|_| \___/|_| |_|\__,_|\__,_|\__|
20:02:13.170 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
20:02:13.201 [main] INFO com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@67ddb171
20:02:13.201 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
20:02:13.205 [main] INFO i.m.flyway.AbstractFlywayMigration - Running migrations for database with qualifier [default]
20:02:13.213 [main] INFO org.flywaydb.core.FlywayExecutor - Database: jdbc:mysql://localhost:3306/db (MySQL 8.3)
20:02:13.220 [main] WARN o.f.c.i.database.base.Database - Flyway upgrade recommended: MySQL 8.3 is newer than this version of Flyway and support has not been tested. The latest supported version of MySQL is 8.1.
20:02:13.230 [main] INFO o.f.core.internal.command.DbValidate - Successfully validated 1 migration (execution time 00:00.008s)
20:02:13.245 [main] INFO o.f.core.internal.command.DbMigrate - Current version of schema `db`: 1
20:02:13.246 [main] INFO o.f.core.internal.command.DbMigrate - Schema `db` is up to date. No migration necessary.
curl -X "POST" "http://localhost:8080/genres" \
-H 'Content-Type: application/json; charset=utf-8' \
-d $'{ "name": "music" }'curl -X "POST" "http://localhost:8080/genres" \
-H 'Content-Type: application/json; charset=utf-8' \
-d $'{ "name": "music#1" }'DB Browser:

Web Browser:

–
