Expert’s Guide to Create and Connect a Micronaut Application to the Oracle Cloud Infrastructure MySQL HeatWave Service

This guide describes how to create a database application using the Graal Development Kit for Micronaut (GDK). The application presents REST endpoints and stores data in an Oracle Cloud Infrastructure MySQL database using Micronaut® Data.

Note: The guide assumes that the reader is familiar with using the Oracle Cloud Infrastructure Command Line Interface (CLI).

MySQL HeatWave is a fully-managed Oracle Cloud Infrastructure database service developed and supported by the MySQL team in Oracle.

Micronaut Data is a database access toolkit that uses ahead-of-time compilation to precompute queries for repository interfaces that are then executed by a thin, lightweight runtime layer. Micronaut Data supports the following back ends: JPA (Hibernate and Hibernate Reactive); SQL (JDBC, R2DBC); and MongoDB.

Prerequisites #

  • JDK 17 or higher. See Setting up Your Desktop.
  • An Oracle Cloud Infrastructure account. See Setting up Your Cloud Accounts.
  • The Oracle Cloud Infrastructure CLI installed with local access configured.
  • An Oracle Cloud Infrastructure compartment. Ensure appropriate permission is granted to your user account to manage Oracle MySQL HeatWave, Compute instances, and Virtual Cloud Networks in the compartment.
  • A Virtual Cloud Network (VCN) in Oracle Cloud Infrastructure with a single regional public subnet, for use by Compute and MySQL HeatWave.
  • A Docker-API compatible container runtime such as Rancher Desktop or Docker to build a GraalVM Native Executable.
  • The GDK CLI. See Setting up Your Desktop. (Optional.)

Follow the steps below to create the application from scratch. However, you can also download the completed example:

A note regarding your development environment

Consider using Visual Studio Code, which provides native support for developing applications with the Graal Development Kit extension.

Note: If you use IntelliJ IDEA, enable annotation processing.

Windows platform: The GDK guides are compatible with Gradle only. Maven support is coming soon.

1. Create the Application #

Create an application using the GDK Launcher.

  1. Open the GDK Launcher in advanced mode.

  2. Create a new project using the following selections.
    • Project Type: Application (Default)
    • Project Name: oci-db-demo
    • Base Package: com.example (Default)
    • Clouds: OCI
    • Build Tool: Gradle (Groovy) or Maven
    • Language: Java (Default)
    • Test Framework: JUnit (Default)
    • Java Version: 17 (Default)
    • Micronaut Version: (Default)
    • Cloud Services: Database
    • Features: GraalVM Native Image and MySQL
    • Sample Code: Yes (Default)
  3. Click Generate Project, then click Download Zip. The GDK Launcher creates a Java project with the default package com.example in a directory named oci-db-demo. The application ZIP file will be downloaded to your default downloads directory. Unzip it, open it in your code editor, and proceed to the next steps.

Alternatively, use the GDK CLI as follows:

gdk create-app com.example.oci-db-demo \
    --clouds=oci \
    --services=database \
    --features=graalvm,mysql \
    --build=gradle \
    --jdk=17 \
    --lang=java
gdk create-app com.example.oci-db-demo \
    --clouds=oci \
    --services=database \
    --features=graalvm,mysql \
    --build=maven \
    --jdk=17 \
    --lang=java

Open the micronaut-cli.yml file, you can see what features are packaged with the application:

features: [app-name, data, data-jdbc, flyway, gdk-bom, gdk-license, gdk-oci-cloud-app, gdk-oci-database, graalvm, http-client-test, java, java-application, jdbc-hikari, junit, logback, maven, maven-enforcer-plugin, micronaut-aot, mysql, netty-server, properties, readme, serialization-jackson, shade, test-resources, validation]

The GDK Launcher creates a multi-module project with two subprojects: oci for Oracle Cloud Infrastructure, and lib for common code and configuration shared across cloud platforms. You develop the application logic in the lib subproject, and keep the Oracle Cloud Infrastructure-specific configurations in the oci subproject. If you enable sample code generation, the GDK Launcher creates the main controller, repository interface, entity, service classes, and tests for you. Consider checking this guide where each sample class is closely examined.

1.1. Configure Datasources #

The GDK Launcher defined default datasources in the oci/src/main/resources/application.properties file. The GDK Launcher also included and enabled Flyway to perform migrations on the default datasources. It uses the Micronaut integration with Flyway to automate schema changes and significantly simplify schema management tasks, such as migrating, rolling back, and reproducing in multiple environments:

flyway.datasources.default.enabled=true

Note: Flyway migrations are not compatible with the default automatic schema generation. So, in the oci/src/main/resources/application.properties file, either delete the datasources.default.schema-generate=CREATE_DROP line or change that line to datasources.default.schema-generate=NONE to ensure that only Flyway manages your schema:

Flyway migration is automatically triggered before your application starts. Flyway reads migration file(s) in the lib/src/main/resources/db/migration/ directory. The migration file with the database schema, lib/src/main/resources/db/migration/V1__schema.sql, was also created for you by the GDK Launcher.

DROP TABLE IF EXISTS genre;

CREATE TABLE genre (
   id    BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
   name  VARCHAR(255) NOT NULL UNIQUE
);

During application startup, Flyway runs the commands in the SQL file and creates the schema needed for the application.

2. Create an Oracle MySQL DB System #

You will create your Oracle MySQL DB System using the Oracle Cloud Infrastructure CLI. See the MySQL Database Service CLI reference for more information.

According to the documentation, the create command requires three parameters: compartment OCID, shape name, and subnet OCID. However, to reduce the number of commands, three more parameters are needed: the DB system configuration OCID, the availability domain, the database initial size, admin username, and password.

2.1. Compartment OCID #

Find the OCID of the compartment where you will deploy the MySQL DB System. It is expected that you have already created a compartment in Oracle Cloud Infrastructure (see the Prerequisites).

Run the following command to list all the compartments in the root:

oci iam compartment list

Find the required compartment by its name or description in the JSON output. It should look like this:

{
  "compartment-id": "ocid1.tenancy.oc1..aaaaaaaaud4g4e5ovjaw...",
  "defined-tags": {},
  "description": "Your Compartment Name",
  "freeform-tags": {},
  "id": "ocid1.compartment.oc1..aaaaaaaarkh3s2wcxbbm...",
  "inactive-status": null,
  "is-accessible": null,
  "lifecycle-state": "ACTIVE",
  "name": "compartment-name",
  "time-created": "2021-05-02T23:54:28.392000+00:00"
}

The OCID is the value of the id property; the compartment-id property is the parent compartment.

For convenience, save the compartment OCID as an environment variable:

export C=ocid1.compartment.oc1.aaaaaaaarkh3s2wcxbbm...
set C=ocid1.compartment.oc1.aaaaaaaarkh3s2wcxbbm...
$ENV:C = "ocid1.compartment.oc1.aaaaaaaarkh3s2wcxbbm..."

In the examples below we use the Linux/macOS syntax for environment variables (for example, -c $C). If you use Windows cmd, change those to -c %C% (but no change needed if using PowerShell).

2.2. Database Shape Name #

Find the shape name for the MySQL DB System. Run this command to list the available shapes in your compartment:

oci mysql shape list -c $C

Use the smallest size and shape (8 GB, 1 CPU):

{
  "cpu-core-count": 1,
  "is-supported-for": [
    "DBSYSTEM"
  ],
  "memory-size-in-gbs": 8,
  "name": "MySQL.VM.Standard.E3.1.8GB"
}

2.3. Database System Configuration OCID #

Find the OCID of the configuration for the MySQL DB System. Run this command to list the available configurations in your compartment:

oci mysql configuration list -c $C

Use the id of the smallest configuration:

{
  "compartment-id": null,
  "defined-tags": null,
  "description": "Default Standalone configuration for the MySQL.VM.Standard.E3.1.8GB MySQL Shape",
  "display-name": "MySQL.VM.Standard.E3.1.8GB.Standalone",
  "freeform-tags": null,
  "id": "ocid1.mysqlconfiguration.oc1..aaaaaaaalwzc2a22xqm5...",
  "lifecycle-state": "ACTIVE",
  "shape-name": "MySQL.VM.Standard.E3.1.8GB",
  "time-created": "2018-09-21T10:00:00+00:00",
  "time-updated": null,
  "type": "DEFAULT"
}

2.4. Subnet OCID #

To find the subnet OCID, first find the OCID of the VCN. It is expected that you have already created a Virtual Cloud Network (VCN) in Oracle Cloud Infrastructure with a single regional public subnet (see the Prerequisites).

Run this command to list the available VCNs in your compartment:

oci network vcn list -c $C

Use the id of the required VCN:

{
  "cidr-block": "10.0.0.0/16",
  "cidr-blocks": [
    "10.0.0.0/16"
  ],
  "compartment-id": "ocid1.compartment.oc1.aaaaaaaarkh3s2wcxbbm...",
  "default-dhcp-options-id": "ocid1.dhcpoptions.oc1.iad.aaaaaaaahosh4fpep4jz...",
  "default-route-table-id": "ocid1.routetable.oc1.iad.aaaaaaaaet7wmwm27vzc...",
  "default-security-list-id": "ocid1.securitylist.oc1.iad.aaaaaaaadlkscc7uktdd...",
  "defined-tags": {},
  "display-name": "vcn-20210504-1214",
  "dns-label": "vcn05041217",
  "freeform-tags": {},
  "id": "ocid1.vcn.oc1.iad.amaaaaaabnqp5kqam6cm...",
  "ipv6-cidr-blocks": null,
  "lifecycle-state": "AVAILABLE",
  "time-created": "2021-05-04T16:17:08.461000+00:00",
  "vcn-domain-name": "vcn05041217.oraclevcn.com"
}

Then use the VCN OCID to find the OCID of the subnet. Run this command to list the subnets in your VCN:

oci network subnet list -c $C \
    --vcn-id ocid1.vcn.oc1.iad.amaaaaaabnqp5kqam6cm...

Use the id of the required subnet:

{
  ...
  "cidr-block": "10.0.0.0/24",
  "compartment-id": "ocid1.compartment.oc1..aaaaaaaarkh3s2wcxbbm...",
  "display-name": "subnet-20210504-1214",
  "id": "ocid1.subnet.oc1.iad.aaaaaaaaxgumfzpn6sul...",
  "time-created": "2021-05-04T16:17:10.593000+00:00",
  "vcn-id": "ocid1.vcn.oc1.iad.amaaaaaabnqp5kqam6cm...",
  ...
  "security-list-ids": [
    "ocid1.securitylist.oc1.iad.aaaaaaaadlkscc7uktdd..."
  ],
  ...
}

Save the OCID of the subnet in the response—you will need it in a later step.

2.5. Availability Domain #

Find the availability domain where the MySQL DB System will be created. Run this command to list the availability domains in your compartment:

oci iam availability-domain list -c $C

Use the name of the availability domain you want:

{
  "compartment-id": "ocid1.compartment.oc1..aaaaaaaarkh3s2wcxbbm...",
  "id": "ocid1.availabilitydomain.oc1..aaaaaaaauvt2n7pijol7...",
  "name": "nFuS:US-ASHBURN-AD-1"
}

2.6. Create a MySQL DB System #

The last required parameters are the admin username and password.

Note: The username must be 1-32 characters, and it must not contain ', `, ", or any of the following reserved names: ocirpl, ociadmin, administrator, mysql.sys, mysql.session, or mysql.infoschema. The password must be 8-32 characters and contain at least one uppercase, one lowercase, one numeric, and one special character.

Additionally, specify the display name (because the generated name will be something like mysqldbsystem20220203163902). Choose a name such as “GDK_Guide_MySQL”. Also specify the initial database size; it must be at least 50 GB.

Run the create command with your OCIDs and other parameters substituted:

oci mysql db-system create -c $C \
    --shape-name MySQL.VM.Standard.E3.1.8GB \
    --configuration-id ocid1.mysqlconfiguration.oc1..aaaaaaaalwzc2a22xqm5... \
    --subnet-id ocid1.subnet.oc1.iad.aaaaaaaaxgumfzpn6sul... \
    --admin-username <your username> \
    --admin-password <your password> \
    --availability-domain nFuS:US-ASHBURN-AD-1 \
    --data-storage-size-in-gbs 50 \
    --display-name GDK_Guide_MySQL

The response should look like this:

{
  "data": {
    "analytics-cluster": null,
    "availability-domain": "nFuS:US-ASHBURN-AD-1",
    "backup-policy": {
      "defined-tags": null,
      "freeform-tags": null,
      "is-enabled": true,
      "retention-in-days": 7,
      "window-start-time": "07:11"
    },
    "channels": [],
    "compartment-id": "ocid1.compartment.oc1..aaaaaaaarkh3s2wcxbbm...",
    "configuration-id": "ocid1.mysqlconfiguration.oc1..aaaaaaaalwzc2a22xqm5...",
    "current-placement": {
      "availability-domain": null,
      "fault-domain": null
    },
    "data-storage-size-in-gbs": 50,
    "defined-tags": {},
    "description": null,
    "display-name": "GDK_Guide_MySQL",
    "endpoints": [],
    "fault-domain": null,
    "freeform-tags": {},
    "heat-wave-cluster": null,
    "hostname-label": null,
    "id": "ocid1.mysqldbsystem.oc1.iad.aaaaaaaa2pq3a37hftut...",
    "ip-address": null,
    "is-analytics-cluster-attached": false,
    "is-heat-wave-cluster-attached": false,
    "is-highly-available": false,
    "lifecycle-details": null,
    "lifecycle-state": "CREATING",
    "maintenance": {
      "window-start-time": "WEDNESDAY 07:09"
    },
    "mysql-version": null,
    "port": null,
    "port-x": null,
    "shape-name": "MySQL.VM.Standard.E3.1.8GB",
    "source": null,
    "subnet-id": "ocid1.subnet.oc1.iad.aaaaaaaaxgumfzpn6sul...",
    "time-created": "2022-02-03T16:39:02.762000+00:00",
    "time-updated": "2022-02-03T16:39:02.762000+00:00"
  },
  "etag": "0dea57803672c12742f7710f342bf...",
  "opc-work-request-id": "ocid1.mysqlworkrequest.oc1.iad.eb66e373-7274-40a..."
}

Note that the value for lifecycle-state is CREATING, so not all information is available at this stage (for example, IP address). Save the database OCID from the id property in the response.

2.7. Retrieve Information Describing the MySQL DB System #

Wait for the MySQL DB System to be provisioned, then run the get command with the OCID of the MySQL DB System as the argument to the --db-system-id option:

oci mysql db-system get \
    --db-system-id ocid1.mysqldbsystem.oc1.iad.aaaaaaaa2pq3a37hftut...

When the lifecycle-state is ACTIVE, make note of ip-address and port (the latter should default to 3306):

"ip-address": "10.0.0.6",
"lifecycle-state": "ACTIVE",
"port": 3306,
"port-x": 33060

2.8. Create an Ingress Rule #

To allow your application to connect to the MySQL DB System, create an ingress rule for port 3306 in the subnet.

Use the OCID of the security list you will update (that you saved earlier when finding the subnet OCID) to retrieve the current state:

oci network security-list get --security-list-id ocid1.securitylist.oc1.iad.aaaaaaaadlkscc7uktdd...

The output should look like this:

...
"id": "ocid1.securitylist.oc1.iad.aaaaaaaadlkscc7uktdd...",
"ingress-security-rules": [
  {
    "description": null,
    "icmp-options": null,
    "is-stateless": false,
    "protocol": "6",
    "source": "0.0.0.0/0",
    "source-type": "CIDR_BLOCK",
    "tcp-options": {
      "destination-port-range": {
        "max": 22,
        "min": 22
      },
      "source-port-range": null
    },
    "udp-options": null
  },
  ...
],
"lifecycle-state": "AVAILABLE",
...

The update command replaces the existing rules with the those specified in the command, so the command must include the existing rules and a new one for port 3306.

Use a text editor to save the JSON list value of ingress-security-rules to a file, for example ingress.json. Add a new object to the JSON list for the ingress rule on port 3306:

{
  "description": "MySQL",
  "isStateless": false,
  "protocol": "6",
  "source": "10.0.0.0/16",
  "sourceType": "CIDR_BLOCK",
  "tcpOptions": {
    "destination-port-range": {
      "max": 3306,
      "min": 3306
    }
  }
}

The final contents of ingress.json should look like this (it will have different rules, but will likely include one for SSH on port 22 and ICMP):

[
  {
    "description": null,
    "icmp-options": null,
    "is-stateless": false,
    "protocol": "6",
    "source": "0.0.0.0/0",
    "source-type": "CIDR_BLOCK",
    "tcp-options": {
      "destination-port-range": {
        "max": 22,
        "min": 22
      },
      "source-port-range": null
    },
    "udp-options": null
  },
  {
    "description": null,
    "icmp-options": {
      "code": 4,
      "type": 3
    },
    "is-stateless": false,
    "protocol": "1",
    "source": "0.0.0.0/0",
    "source-type": "CIDR_BLOCK",
    "tcp-options": null,
    "udp-options": null
  },

  ...

  {
    "description": "MySQL",
    "isStateless": false,
    "protocol": "6",
    "source": "10.0.0.0/16",
    "sourceType": "CIDR_BLOCK",
    "tcpOptions": {
      "destination-port-range": {
        "max": 3306,
        "min": 3306
      }
    }
  }
]

Run this command to add a new ingress rule:

oci network security-list update \
    --security-list-id ocid1.securitylist.oc1.iad.aaaaaaaadlkscc7uktdd... \
    --ingress-security-rules file://ingress.json

3. Create a Compute Instance #

There is no direct way to externally connect to a MySQL database, so you need to create a Compute instance in Oracle Cloud Infrastructure to host your application. Later, you will install a MySQL client on that Compute instance and connect your application to the MySQL DB System.

The Compute service comes as part of Always Free Eligible Oracle Cloud Infrastructure services (you do not need a paid account).

  1. In the Oracle Cloud console, open the navigation menu. Go to Compute, then Instances.

  2. Click Create instance. You are prompted to enter information to describe a new Compute instance. Enter the following information:

    a. Name: Enter “gdk-demo”.

    b. Create in compartment: Select the compartment from the compartment drop-down list, or use the default.

    c. Placement: Use the default.

    d. Image and shape: Use the default.

    e. Networking: Make sure to use the same subnet as the one where you created the MySQL database, otherwise the application will not be able to access the database. Your MySQL database private IP is used to connect the MySQL client.

    f. Add SSH keys: Select “Generate a key pair for me”, then click Save private key to save the private key locally in a file named ssh-key-(date).key.

    g. Boot Volume: Use the default.

  3. Click Create.
  4. When the instance is active, take note of the Public IP address and Private IP address displayed in the Instance information tab (click Copy).
  5. Click Subnet, under “Primary VNIC” in the Instance Information tab.
  6. Click Default Security List .., under Security Lists.
  7. Click Add Ingress Rules, under Ingress Rules. Enter the following information:

    a. Source CIDR: Enter “0.0.0.0/0”

    b. Destination Port Range: Enter “8080”

    c. Description: Enter “micronaut”

  8. Click Add Ingress Rules.

  9. Connect to your newly created Compute instance, using the instance’s Public IP address:
     ssh -i /path/to/ssh-key-(date).key opc@<COMPUTE_INSTANCE_PUBLIC_IP>
    

    For more information, see Connecting to Your Linux Instance Using SSH.

  10. Once connected, install Oracle GraalVM for JDK 17 (it will be required at a later step).

    On Oracle Linux 8 and 9, run these commands one by one:

    sudo dnf update -y oraclelinux-release-el8
    sudo dnf config-manager --set-enabled ol8_codeready_builder
    sudo dnf install graalvm-17-native-image
    

    Confirm that the installed package size is correct by entering “yes” at the prompt.

    On Oracle Linux 7.9, run:

    sudo yum -y install oracle-softwarecollection-release-el7
    sudo yum install devtoolset-10
    echo 'source scl_source enable devtoolset-10' >> ~/.bashrc
    sudo yum install graalvm-17-native-image
    
  11. Open up the firewall to port 8080:
       sudo firewall-cmd --permanent --zone=public --add-port=8080/tcp
       sudo firewall-cmd --reload
    

Now you have a ready-to-go Compute instance with the Java runtime. For more information, see the Oracle GraalVM on OCI Compute Instances with Oracle Linux guide.

4. Configure a MySQL Database #

Now connect your application to the MySQL DB System. Flyway will create the database tables the first time the application starts, but you must create the database and a database user first.

4.1. MySQL Client #

  1. Install the MySQL client. From the terminal connected to your Compute instance, run:
     sudo yum install mysql
    
  2. Connect to the MySQL client with admin username and password you chose earlier, and your MySQL DB System private IP address:
     mysql --host <MySQL_PRIVATE_IP> -u <admin_username> -p
    

    Where <MySQL_PRIVATE_IP> is the private IP address of your Oracle Cloud Infrastructure MySQL DB System.

Once connected, you are taken to the MySQL database command prompt.

4.2. Create a Database and User #

  1. From the MySQL database command prompt, create the database (use any valid database name, for example, gdkDB):
     CREATE DATABASE gdkDB;
    
  2. Create a database user (use any valid MySQL username, for example, guide_user, and a valid password):
     CREATE USER 'guide_user'@'<COMPUTE_INSTANCE_PRIVATE_IP>' IDENTIFIED BY '<user password>';
    

    Where <COMPUTE_INSTANCE_PRIVATE_IP> is the private IP address of your Compute instance as noted earlier.

  3. Grant access to the database for the new user:
     GRANT ALL ON gdkDB.* TO 'guide_user'@'<COMPUTE_INSTANCE_PRIVATE_IP>';
    
  4. Exit the MySQL command prompt by entering “exit”.

5. Run the Application #

With almost everything in place, you can compile and run the application from a JAR file.

  1. Create an executable JAR file that includes all dependencies:

    ./gradlew :oci:shadowJar
    ./mvnw install -pl lib -am
    ./mvnw package -pl oci -DskipTests
  2. From a local terminal, copy the JAR file from your computer to the Compute instance using scp:

    scp -i /path/to/ssh-key-*.key path/to/oci-db-demo/oci/build/libs/oci-1.0-SNAPSHOT-all.jar opc@COMPUTE_INSTANCE_PUBLIC_IP:~/application.jar
    scp -i /path/to/ssh-key-*.key path/to/oci-db-demo/oci/target/oci-1.0-SNAPSHOT.jar opc@COMPUTE_INSTANCE_PUBLIC_IP:~/application.jar
  3. Once copied, connect to the Compute instance (if it has disconnected by now):
     ssh -i /path/to/ssh-key-*.key opc@COMPUTE_INSTANCE_PUBLIC_IP
    
  4. Set values for the missing datasources.default.url, datasources.default.username, and datasources.default.password properties by exporting them as environment variables as follows:

    export DATASOURCES_DEFAULT_URL=jdbc:mysql://<MYSQL_DB_SYSTEM_PRIVATE_IP>:3306/<db_name> # 1
    export DATASOURCES_DEFAULT_USERNAME=<username> # 2
    export DATASOURCES_DEFAULT_PASSWORD=<password> # 3
    set DATASOURCES_DEFAULT_URL=jdbc:mysql://<MYSQL_DB_SYSTEM_PRIVATE_IP>:3306/<db_name> # 1
    set DATASOURCES_DEFAULT_USERNAME=<username> # 2
    set DATASOURCES_DEFAULT_PASSWORD=<password> # 3
    $ENV:DATASOURCES_DEFAULT_URL = "jdbc:mysql://<MYSQL_DB_SYSTEM_PRIVATE_IP>:3306/<db_name>" # 1
    $ENV:DATASOURCES_DEFAULT_USERNAME = "<username>" # 2
    $ENV:DATASOURCES_DEFAULT_PASSWORD = "<password>" # 3

    1 Set the value of the datasources.default.url property as the private IP address of MySQL DB System (ip-address from section 2.7) combined with the name of the database, for example, gdkDB.

    2 Set the value of the datasources.default.username property as the username of the user you created, for example, guide_user.

    3 Set the value of the datasources.default.password property as the password of the user you created.

  5. Start the application:
     java -jar application.jar
    
  6. From a local terminal, run this command to test creating and storing a new Genre in the database:
     curl -X "POST" "http://[COMPUTE_INSTANCE_PUBLIC_IP]:8080/genres" \
          -H 'Content-Type: application/json; charset=utf-8' \
          -d $'{ "name": "music" }'
    

    Then list the genres:

     curl [COMPUTE_INSTANCE_PUBLIC_IP]:8080/genres/list
    

This server application presents REST endpoints and stores data in the Oracle MySQL database. Next, you can package this application as a native executable. Deploying as a native executable does not require a JDK to run, so you can copy it to another Linux host and run easily.

6. Generate a Native Executable Using GraalVM #

The GDK supports compiling Java applications ahead-of-time into native executables using GraalVM Native Image. You can use the Gradle plugin for GraalVM Native Image building/Maven plugin for GraalVM Native Image building. Packaged as a native executable, it significantly reduces application startup time and memory footprint.

Prerequisites: Make sure you have installed a GraalVM JDK. The easiest way to get started is with SDKMAN!. For other installation options, visit the Downloads section.

If you build a native executable locally, it will only run on your OS, but isn’t suitable for deployment to a Compute instance. To create deployable native executables, we’ll use a different packaging command that builds the executable inside a container, which we’ll extract from the container to deploy to the cloud.

  1. From a local terminal, generate a native executable by running the following command:

    ./gradlew :oci:dockerBuildNative
    ./mvnw install -pl lib -am
    ./mvnw clean package -pl oci -Dpackaging=docker-native

    You can customize the name of the resulting binary by updating the Maven/Gradle plugin for GraalVM Native Image configuration.

  2. Find the container image ID that you’ll use to extract the native executable from the container image you built by running:

     docker image ls
    

    The container images are listed chronologically with the most recent at the top, so your output will look like this:

     REPOSITORY   TAG      IMAGE ID       CREATED          SIZE
     oci          latest   41c8a902b2e7   32 seconds ago   100MB
    
  3. Extract the native executable by running these commands, replacing <image_id> with the container image ID, for example, 41c8a902b2e7:

     docker create --name container_temp <image_id>
     docker cp container_temp:/app/application .
     docker rm container_temp
    

7. Deploy from a Native Executable #

  1. From a local terminal, copy the native executable from your computer to the Compute instance using scp:

     scp -i /path/to/ssh-key-*.key application opc@COMPUTE_INSTANCE_PUBLIC_IP:~/
    
  2. Once copied, connect to the Compute instance (if it has disconnected by now):
     ssh -i /path/to/ssh-key-*.key opc@COMPUTE_INSTANCE_PUBLIC_IP
    
  3. Stop the application running from the JAR file if it’s still running.

  4. Run the application from the native executable:
     ./application
    
  5. From a local terminal, run this command to test creating and storing a new Genre in the database:
     curl -X "POST" "http://[COMPUTE_INSTANCE_PUBLIC_IP]:8080/genres" \
          -H 'Content-Type: application/json; charset=utf-8' \
          -d $'{ "name": "music" }'
    

    Then list all genres:

     curl [COMPUTE_INSTANCE_PUBLIC_IP]:8080/genres/list
    

    As a reminder, you do not need to install a Java VM to run the application from a native executable. The native executable is a self-contained binary. Deploying from a native executable significantly reduces application startup time and memory footprint.

8. Clean up Cloud Resources #

When you finish using the MySQL DB System, you can delete it using the CLI. Run the following command:

oci mysql db-system delete \
    --db-system-id ocid1.mysqldbsystem.oc1.iad.aaaaaaaa2pq3a37hftut...

The output should look like this:

{
"opc-work-request-id": "ocid1.mysqlworkrequest.oc1.iad.e68d5dc7-92be-45..."
}

Summary #

This guide demonstrated how to use the GDK to create and connect a database application that stores data in an Oracle Cloud Infrastructure MySQL database using Micronaut Data. You also learned how to package this application into a native executable and deploy it from a virtual machine.