The original version of this tutorial used Hibernate 5's org.hibernate.tool.hbm2ddl.SchemaExport class. Modern Hibernate no longer requires application code to work directly with that old schema-export API.

With Hibernate ORM 7.4 and Jakarta Persistence 3.2, a much cleaner programmatic option is available through SchemaManager. Once a SessionFactory is created, you can ask Hibernate to create, validate, truncate or drop the database objects mapped by your entities.

Hibernate 7.4 dependency

This example targets Hibernate ORM 7.4.6.Final and Java 17 or newer.

The Hibernate platform BOM keeps Hibernate artifact versions aligned:

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.hibernate.orm</groupId>
      <artifactId>hibernate-platform</artifactId>
      <version>7.4.6.Final</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

<dependencies>
  <dependency>
    <groupId>org.hibernate.orm</groupId>
    <artifactId>hibernate-core</artifactId>
  </dependency>

  <!-- Add the JDBC driver required by your database. -->
</dependencies>

Hibernate 7 uses Jakarta Persistence APIs, so entity annotations come from jakarta.persistence, not the older javax.persistence package.

Create a Hibernate entity

For this example, assume Hibernate manages a simple Player entity:

package com.mcnz.jpa.examples;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;

@Entity
public class Player {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    public Player() {
    }

    public Player(String name) {
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Configure Hibernate 7 database settings

The familiar Hibernate native bootstrap APIs still work. A StandardServiceRegistry contains the database and Hibernate configuration used to build the metadata and SessionFactory.

For MySQL, the settings can look like this:

Map<String, Object> settings = new HashMap<>();

settings.put(
    "hibernate.connection.url",
    "jdbc:mysql://localhost:3306/hibernate_examples"
);
settings.put(
    "hibernate.connection.username",
    "root"
);
settings.put(
    "hibernate.connection.password",
    "password"
);
settings.put(
    "hibernate.show_sql",
    "true"
);
settings.put(
    "hibernate.format_sql",
    "true"
);

Notice that this example does not explicitly configure com.mysql.jdbc.Driver. That old MySQL driver class name is obsolete, and modern JDBC drivers are normally discovered automatically.

It also does not force a Hibernate MySQL dialect. Hibernate can usually determine the appropriate dialect from JDBC metadata. Explicit dialect configuration is still possible when an application has a specific reason to override automatic detection.

Build the ServiceRegistry and Metadata

The basic Hibernate bootstrap sequence remains recognizable to anyone who used Hibernate 5:

StandardServiceRegistry serviceRegistry =
    new StandardServiceRegistryBuilder()
        .applySettings(settings)
        .build();

MetadataSources metadataSources =
    new MetadataSources(serviceRegistry);

metadataSources.addAnnotatedClass(Player.class);

Metadata metadata =
    metadataSources.buildMetadata();

The metadata describes Hibernate's mapped domain model. From that metadata, Hibernate can create a SessionFactory.

SessionFactory sessionFactory =
    metadata.buildSessionFactory();

Replace SchemaExport with SchemaManager

This is where the modern Hibernate example differs most from the old Hibernate 5 version.

Instead of creating a SchemaExport, an EnumSet<TargetType> and a SchemaExport.Action, obtain the schema manager directly from the SessionFactory:

SchemaManager schemaManager =
    sessionFactory.getSchemaManager();

The schema can then be created programmatically:

schemaManager.create(false);

The boolean argument controls whether Hibernate should attempt to create the database schema namespace itself. Passing false assumes the schema or database already exists and creates the mapped objects such as tables and constraints inside it.

Other useful operations include:

schemaManager.validate();

schemaManager.truncate();

schemaManager.drop(false);

validate() checks whether the mapped database objects match the entity mappings. truncate() removes table data while keeping the mapped schema. drop(false) drops the mapped database objects but leaves the schema namespace itself intact.

Complete Hibernate 7 schema creation example

Here is the complete example using Hibernate ORM 7.4:

package com.mcnz.jpa.examples;

import java.util.HashMap;
import java.util.Map;

import org.hibernate.SessionFactory;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.relational.SchemaManager;

public class HibernateSchemaManagerExample {

    public static void main(String[] args) {

        Map<String, Object> settings = new HashMap<>();

        settings.put(
            "hibernate.connection.url",
            "jdbc:mysql://localhost:3306/hibernate_examples"
        );
        settings.put(
            "hibernate.connection.username",
            "root"
        );
        settings.put(
            "hibernate.connection.password",
            "password"
        );
        settings.put(
            "hibernate.show_sql",
            "true"
        );
        settings.put(
            "hibernate.format_sql",
            "true"
        );

        StandardServiceRegistry serviceRegistry =
            new StandardServiceRegistryBuilder()
                .applySettings(settings)
                .build();

        try {
            Metadata metadata =
                new MetadataSources(serviceRegistry)
                    .addAnnotatedClass(Player.class)
                    .buildMetadata();

            try (SessionFactory sessionFactory =
                     metadata.buildSessionFactory()) {

                SchemaManager schemaManager =
                    sessionFactory.getSchemaManager();

                schemaManager.drop(false);
                schemaManager.create(false);
                schemaManager.validate();
            }

        } finally {
            StandardServiceRegistryBuilder.destroy(
                serviceRegistry
            );
        }
    }
}

This example deliberately drops and recreates the mapped objects, which makes it useful for demonstrations and automated tests. It is not appropriate for a production database containing valuable data.

Automatic schema generation is even simpler

If you do not specifically need to invoke schema operations from Java code, Hibernate can perform schema management automatically when the SessionFactory starts.

For example:

hibernate.hbm2ddl.auto=create

Common values include:

Value Behavior
none Do not perform automatic schema management.
validate Check the existing schema against the entity mappings.
update Attempt to update the existing schema.
create Drop existing mapped objects and create them again.
create-drop Create the schema at startup and drop it when the factory shuts down.

Jakarta Persistence schema-generation settings

Hibernate 7 also implements the standard Jakarta Persistence schema-generation settings. For example:

jakarta.persistence.schema-generation.database.action=create

The Jakarta Persistence standard supports schema actions including none, create, drop and drop-and-create.

Hibernate schema generation in production

Hibernate's schema tooling is extremely useful for tests, prototypes and local development. For production databases, incremental migration tools such as Flyway or Liquibase are usually a better fit because schema changes can be reviewed, versioned and applied in controlled steps.

The modern lesson is therefore a little different from the original Hibernate 5 tutorial. If you need programmatic schema operations, use SessionFactory.getSchemaManager(). If you simply need automatic schema creation during development, configuration properties are easier. For production evolution of an existing database, prefer explicit migration scripts.