Skip to content

ariga/atlas-provider-hibernate

Repository files navigation

atlas-provider-hibernate

Load Hibernate ORM schemas into an Atlas project.

Use-cases

  1. Declarative migrations - use a Terraform-like atlas schema apply to apply your Hibernate schema to the database.
  2. Automatic migration planning - use atlas migrate diff to automatically plan a migration from
    the current database version to the schema declared by the code.

Installation

Install Atlas from macOS or Linux by running:

curl -sSf https://atlasgo.sh | sh

See atlasgo.io for more installation options.

Gradle

Add the provider to your Gradle project:

plugins {
   id("io.atlasgo.hibernate-provider-gradle-plugin") version "0.1"
}

This plugin adds a task named schema to the project. Running this task will print the schema generated by Hibernate using entities and configurations taken from the runtime classpath.

The task implements the JavaExec task and the plugin does not make any configuration or dependency changes to the project.

Let's check that the task works by running the following command:

./gradlew -q schema

Maven

To add the provider to your project, you need to add the hibernate-provider-maven-plugin plugin to the pluginManagement section in your pom.xml:

 <build>
     <pluginManagement>
         <plugins>
             <plugin>
                 <groupId>io.atlasgo</groupId>
                 <artifactId>hibernate-provider-maven-plugin</artifactId>
                 <version>0.3.8</version>
             </plugin>
         </plugins>
     </pluginManagement>
 </build>

A complete example can be seen in the examples folder.

The plugin adds a MOJO with a goal named 'schema' to your project.

You should add the plugin to pluginManagement as it is not meant to be running in any specific phase of the build process.

Let's check that the goal is installed correctly by running:

mvn help:describe -Dplugin=hibernate-provider -Dgoal=schema

Configuration

By default, the task is configured to scan the entire runtimeClasspath configuration and look for Jakarta (JPA) entities. The task does not require a database connection, but it does need to configure Hibernate with a specific database dialect. There are several flags and hooks you can use to configure how Hibernate is being initialized inside the task, we will explore these later.

Setup Atlas

In your project directory, create a new file named atlas.hcl with the following contents:

data "external_schema" "hibernate" {
   program = [
       "./gradlew", 
       "-q",
       "schema"
   ]
}
	
env "hibernate" {
   src = data.external_schema.hibernate.url
   dev = "docker://mysql/8/dev"
   migration {
      dir = "file://migrations"
   }
   format {
      migrate {
         diff = "{{ sql . \"  \" }}"
      }
   }
}

For Maven, change the program field to use mvn instead of gradlew:

data "external_schema" "hibernate" {
   program = [
       "mvn",
       "compile",
       "-q",
       "hibernate-provider:schema"
   ]
}

Choosing the dialect

The above configuration uses a dev database that is configured to use MySQL. The SchemaTask is initializing Hibernate which typically uses the file hibernate.properties from your project to determine which dialect it should be using.

If needed, you can override the dialect specification using the --properties flag.

For example, for PostgreSQL:

  • define jakarta.persistence.database-product-name=PostgreSQL in hibernate.properties
  • define docker://postgres/15/dev?search_path=public as atlas dev database in atlas.hcl

Now we can check that the configuration is working by inspecting the schema:

atlas schema inspect -w --env hibernate --url env://src

Flags

Both the Gradle and Maven plugins support the following flags:

  • packages - List of package names (for example org.example.myservice.model). If specified, only classes inside these packages will be considered during the entity scan. Given packages must be part of the classpath
  • classes - List of classes that will be added to the entity scan explicitly. Ignores the packages argument.
  • properties - Name of a properties file used to override the default properties read by Hibernate.
  • registry-builder - FQDN of a class that implements java.util.Function.Function<java.util.Properties, org.hibernate.service.ServiceRegistry>. Used when you need to override the default ServiceRegistry initialized by the task. Useful if you have a custom initialization process for Hibernate. The properties parameter is the default settings used by the plugin, including ones read from the properties parameter.
  • metadata-builder - FQDN of a class that implements java.util.Function.Function<org.hibernate.service.ServiceRegistry, org.hibernate.boot.Metadata>. Used when you need to override the default Metadata used by the task. mutually exclusive with packages and classes arguments.
  • enable-table-generators - Atlas does not currently support generated fields that require data initialization such as GenerationType.SEQUENCE, GenerationType.TABLE, and Generation.AUTO. By default, the provider will throw an exception if it finds unsupported statements. By enabling this flag, the provider will print the unsupported SQL statements.
    • Make sure to apply the ignored statements (using atlas migrate --env hibernate diff --edit) See more information on manual migrations here

You can configure these flags by adding arguments in the external_schema block in atlas.hcl:

data "external_schema" "hibernate" {
   program = [
       "./gradlew",
       "--properties", "other.properties"
       "--classes", "org.example.model.Person"
       "-q",
       "schema"
   ]
}

Or, if you are using Maven:

data "external_schema" "hibernate" {
   program = [
       "mvn",
       "compile",
       "-Dproperties", "other.properties"
       "-Dclasses", "org.example.model.Person"
       "-q",
       "hibernate-provider:schema"
   ]
}

Note: The '-q' flag is important, without it, Gradle/Maven will add additional output

Gradle tasks

Alternatively, you can define a new Gradle task:

import io.atlasgo.SchemaTask

tasks.register<SchemaTask>("my_schema") {
    classes = listOf("org.example.model.Person")
}

And refer to this task in atlas.hcl:

data "external_schema" "hibernate" {
   program = [
       "./gradlew",
       "-q",
       "my_schema"
   ]
}

Usage

Once you have the provider installed, you can use it to apply your Hibernate schema to the database:

Apply

You can use the atlas schema apply command to plan and apply a migration of your database to your current Hibernate schema. This works by inspecting the target database and comparing it to the Hibernate schema and creating a migration plan. Atlas will prompt you to confirm the migration plan before applying it to the database.

atlas schema apply --env hibernate -u "mysql://root:password@localhost:3306/mydb"

Where the -u flag accepts the URL to the target database.

Diff

Atlas supports a versioned migration workflow, where each change to the database is versioned and recorded in a migration file. You can use the atlas migrate diff command to automatically generate a migration file that will migrate the database from its latest revision to the current Hibernate schema.

run atlas migrate diff --env hibernate command and observe the migrations directory.

Spring integration

If you are using Spring in your project, you will need to import the hibernate-provider and create a new empty spring application, for example:

@SpringBootApplication
@PropertySource(value = {"classpath:schema-export.properties"})
public class Main {
    public static void main(String[] args) {
        new AnnotationConfigApplicationContext(Main.class);
    }
}

A complete example project is provided here

Try running the example by running: atlas schema inspect -w --env hibernate --url env://src inside the project directory.

We need to configure the spring application so that it will print the schema to stdout. Spring is often configured to load Hibernate during startup, by adding the configuration below, we instruct Spring to generate the schema during initialization and print it to stdout:

spring.jpa.properties.jakarta.persistence.schema-generation.database.action=create
spring.jpa.properties.hibernate.schema_management_tool=io.atlasgo.ConsoleSchemaManagementTool

Configuring the driver with dialect and version can be done either via the same configuration file or via a dedicated Bean. For example, in the example project, this configuration is used for MySQL 8:

spring.jpa.properties.jakarta.persistence.database-product-name=MySQL
spring.jpa.properties.jakarta.persistence.database-major-version=8

In your atlas.hcl file you will need to invoke the Spring application instead of the Gradle/Maven plugin.

License

This project is licensed under the Apache License 2.0.