Automatic Schema Migration Planning for Hibernate
TL;DR
- Hibernate is an ORM library that's widely used in the Java community.
- Atlas is an open-source tool for inspecting, planning, linting and executing schema changes to your database.
- Developers using Hibernate can use Atlas to automatically plan schema migrations for them, based on the desired state of their schema instead of crafting them by hand.
Automatic migration planning for Hibernate
Hibernate is a popular ORM widely used in the Java community. Hibernate allows users to manage their database schemas using its automatic schema generation feature, which is usually sufficient during development and in many simple cases.
However, at some point, teams need more control and decide to employ the versioned migrations methodology, which is a more robust way to manage your database schema. Once this happens, the responsibility for planning migration scripts and making sure they are in line with what Hibernate expects at runtime is moved to developers.
Atlas can automatically plan database schema migrations for developers using Hibernate. Atlas plans migrations by calculating the diff between the current state of the database and its desired state.
In the context of versioned migrations, the current state can be thought of as the database schema that would have been created by applying all previous migration scripts.
The desired schema of your application can be provided to Atlas via an External Schema Data Source, which is any program that can output a SQL schema definition to stdout.
To use Atlas with Hibernate, users can utilize the Hibernate Atlas Provider, a small program that can be used to load the schema of a Hibernate project into Atlas.
In this guide, we will show how Atlas can automatically plan schema migrations for Hibernate users.
Prerequisites
- A local Hibernate project.
If you don't have a Hibernate project handy, check out the Hibernate getting started page
Using the Atlas Hibernate Provider
In this guide, we will use the Hibernate Atlas Provider to automatically plan schema migrations for a Hibernate project.
Using the provider does not require a connection to a live database.
Installation
Install Atlas from macOS or Linux by running:
curl -sSf https://atlasgo.sh | sh
See atlasgo.io for more installation options.
Install the provider by adding this plugin to your Gradle project:
- Gradle Kotlin
- Gradle Groovy
- Maven
plugins {
id("io.atlasgo.hibernate-provider-gradle-plugin") version "0.1"
}
plugins {
id "io.atlasgo.hibernate-provider-gradle-plugin" version "0.1"
}
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>io.atlasgo</groupId>
<artifactId>hibernate-provider-maven-plugin</artifactId>
<version>0.1</version>
</plugin>
</plugins>
</pluginManagement>
</build>
Verify that the plugin is installed by running this command:
- Gradle
- Maven
./gradlew help --task schema
mvn help:describe -Dplugin=hibernate-provider -Dgoal=schema
Library vs Standalone
The Atlas Hibernate Provider can be used in two modes:
- Standalone - In most cases, you can use the provider as a Gradle task (or a Maven Mojo) to output the Hibernate schema and load the schema into Atlas.
- Library - If you are initializing Hibernate in a custom way, using an older version of Hibernate or if you are not using Gradle or Maven, you can use the provider as a library and create a small application that prints the schema to stdout.
Standalone
In your project directory, create a new file named atlas.hcl
with the following contents:
- Gradle
- Maven
data "external_schema" "hibernate" {
program = [
"./gradlew",
"-q",
"schema",
"--properties", "schema-export.properties"
]
}
data "external_schema" "hibernate" {
program = [
"./mvn",
"-q",
"hibernate-provider:schema",
"-Dproperties", "schema-export.properties"
]
}
Depending on your database, add an environment configuration. For example, for MySQL and PostgreSQL:
- MySQL
- PostgreSQL
env "hibernate" {
src = data.external_schema.hibernate.url
dev = "docker://mysql/8/dev"
migration {
dir = "file://migrations"
}
format {
migrate {
diff = "{{ sql . \" \" }}"
}
}
}
env "hibernate" {
src = data.external_schema.hibernate.url
dev = "docker://postgres/15/dev?search_path=public"
migration {
dir = "file://migrations"
}
format {
migrate {
diff = "{{ sql . \" \" }}"
}
}
}
We need to let Atlas and the provider know which SQL dialect should be used. The dialect is specified in the properties file and in the Dev Database that Atlas will use.
Create a resource named schema-export.properties
in src/main/resource
, it should be part of the classpath by default.
Specify the SQL dialect that will be used to initialize Hibernate:
- MySQL
- PostgreSQL
jakarta.persistence.database-product-name=MySQL
jakarta.persistence.database-major-version=8
jakarta.persistence.database-product-name=PostgreSQL
Note: The properties being used might vary depending on the version of Hibernate and the database connector that is configured for your project.
Library mode
We are going to add the provider as a dependency in Gradle (or Maven) and configure Hibernate to use
ConsoleSchemaManagementTool
. In this example, we are going to use Spring to initialize Hibernate.
By default, the
HibernateSchemaManagementTool
will try to connect to the database during initialization and generate the schema into the database. TheConsoleSchemaManagementTool
overrides this behaviour.
Start by adding the provider as a dependency:
- Gradle
- Maven
dependencies {
implementation("io.atlasgo:hibernate-provider:0.1")
}
<dependency>
<groupId>io.atlasgo</groupId>
<artifactId>hibernate-provider</artifactId>
<version>0.1</version>
</dependency>
Create a new file named HibernateSchemaExporter.java
with the following contents:
@SpringBootApplication
@PropertySource(value = {"classpath:schema-export.properties"})
public class HibernateSchemaExporter {
public static void main(String[] args) {
new AnnotationConfigApplicationContext(HibernateSchemaExporter.class);
}
}
Next, create a new resource named schema-export.properties
(make sure it is in the classpath) with this content:
- MySQL
- PostgreSQL
spring.jpa.properties.jakarta.persistence.database-product-name=MySQL
spring.jpa.properties.jakarta.persistence.database-major-version=8
spring.jpa.properties.jakarta.persistence.schema-generation.database.action=create
spring.jpa.properties.hibernate.schema_management_tool=io.atlasgo.ConsoleSchemaManagementTool
spring.jpa.properties.jakarta.persistence.database-product-name=PostgreSQL
spring.jpa.properties.jakarta.persistence.schema-generation.database.action=create
spring.jpa.properties.hibernate.schema_management_tool=io.atlasgo.ConsoleSchemaManagementTool
Lastly, in your project directory, create a new file named atlas.hcl
with the following contents:
data "external_schema" "hibernate" {
program = [
"./gradlew",
"-q",
"bootRun"
]
}
Depending on your database, add an environment configuration. For example, for MySQL and PostgreSQL:
- MySQL
- PostgreSQL
env "hibernate" {
src = data.external_schema.hibernate.url
dev = "docker://mysql/8/dev"
migration {
dir = "file://migrations"
}
format {
migrate {
diff = "{{ sql . \" \" }}"
}
}
}
env "hibernate" {
src = data.external_schema.hibernate.url
dev = "docker://postgres/15/dev?search_path=public"
migration {
dir = "file://migrations"
}
format {
migrate {
diff = "{{ sql . \" \" }}"
}
}
}