Guide to Hibernate 5 Schema Generation

In this guide, You will learn how does Hibernate allows you to generate the database from the entity mappings. In this guide, we will following points.

Table of contents

  1. Generating Schema from Entity Mapping
  2. Importing script files
  3. Database objects
  4. Database-level checks
  5. A default value for a database column
  6. Columns unique constraint
  7. Columns index

1. Generating Schema from Entity Mapping

Traditionally, the process of generating a schema from entity mapping has been called HBM2DDL.
For example, considering the following Domain Model with their schema generation.

Schema generation Domain Model

@Entity(name = "Customer")
public class Customer {

    @Id
    private Integer id;

    private String name;

    @Basic(fetch = FetchType.LAZY)
    private UUID accountsPayableXrefId;

    @Lob
    @Basic(fetch = FetchType.LAZY)
    @LazyGroup("lobs")
    private Blob image;

    //Getters and setters are omitted for brevity

}

@Entity(name = "Person")
public static class Person {

    @Id
    private Long id;

    private String name;

    @OneToMany(mappedBy = "author")
    private List < Book > books = new ArrayList < > ();

    //Getters and setters are omitted for brevity

}

@Entity(name = "Book")
public static class Book {

    @Id
    private Long id;

    private String title;

    @NaturalId
    private String isbn;

    @ManyToOne
    private Person author;

    //Getters and setters are omitted for brevity

}
If the hibernate.hbm2ddl.auto is set to create, Hibernate is going to generate the following database schema:

Auto-generated database schema

create table Customer (
    id integer not null,
    accountsPayableXrefId binary,
    image blob,
    name varchar(255),
    primary key (id)
)

create table Book (
    id bigint not null,
    isbn varchar(255),
    title varchar(255),
    author_id bigint,
    primary key (id)
)

create table Person (
    id bigint not null,
    name varchar(255),
    primary key (id)
)

alter table Book
    add constraint UK_u31e1frmjp9mxf8k8tmp990i unique (isbn)

alter table Book
    add constraint FKrxrgiajod1le3gii8whx2doie
    foreign key (author_id)
    references Person
Basically, hibernate.hbm2ddl.auto Automatically validates or exports schema DDL to the database when the SessionFactory is created. With create-drop, the database schema will be dropped when the SessionFactory is closed explicitly.
So the list of possible options:
  • validate: validate the schema, makes no changes to the database.
  • update: update the schema.
  • create: creates the schema, destroying previous data.
  • create-drop: drop the schema when the SessionFactory is closed explicitly, typically when the application is stopped.

2. Importing script files

To customize the schema generation process, the hibernate.hbm2ddl.import_files configuration property must be used to provide other scripts files that Hibernate can use when the SessionFactory is started.
For instance, considering the following schema-generation.sql import file:

Example: Schema generation import file

create sequence book_sequence start with 1 increment by 1
If we configure Hibernate to import the script above:

Example: Enabling query cache

<property
    name="hibernate.hbm2ddl.import_files"
    value="schema-generation.sql" />
Hibernate is going to execute the script file after the schema is automatically generated.

3. Database objects

Hibernate allows you to customize the schema generation process via the HBM database-object element.

Example: Schema generation HBM database-object

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd" >
<hibernate-mapping>
    <database-object>
        <create>
            CREATE OR REPLACE FUNCTION sp_count_books(
                IN authorId bigint,
                OUT bookCount bigint)
                RETURNS bigint AS
            $BODY$
                BEGIN
                    SELECT COUNT(*) INTO bookCount
                    FROM book
                    WHERE author_id = authorId;
                END;
            $BODY$
            LANGUAGE plpgsql;
        </create>
        <drop></drop>
        <dialect-scope name="org.hibernate.dialect.PostgreSQL95Dialect" />
    </database-object>
</hibernate-mapping>
When the SessionFactory is bootstrapped, Hibernate is going to execute the database-object, therefore creating the sp_count_books function.

4. Database-level checks

Hibernate offers the @Check annotation so that you can specify an arbitrary SQL CHECK constraint which can be defined as follows:

Example: Database check entity mapping example

@Entity(name = "Book")
@Check( constraints = "CASE WHEN isbn IS NOT NULL THEN LENGTH(isbn) = 13 ELSE true END")
public static class Book {

 @Id
 private Long id;

 private String title;

 @NaturalId
 private String isbn;

 private Double price;

 //Getters and setters omitted for brevity

}
Now, if you try to add a Book entity with an isbn attribute whose length is not 13 characters, a ConstraintViolationException is going to be thrown.

Example: Database check failure example

Book book = new Book();
book.setId( 1L );
book.setPrice( 49.99d );
book.setTitle( "High-Performance Java Persistence" );
book.setIsbn( "11-11-2016" );

entityManager.persist( book );
INSERT  INTO Book (isbn, price, title, id)
VALUES  ('11-11-2016', 49.99, 'High-Performance Java Persistence', 1)

-- WARN SqlExceptionHelper:129 - SQL Error: 0, SQLState: 23514
-- ERROR SqlExceptionHelper:131 - ERROR: new row for relation "book" violates check constraint "book_isbn_check"

5. A default value for a database column

With Hibernate, you can specify a default value for a given database column using the @ColumnDefault annotation.

Example: @ColumnDefault mapping example

@Entity(name = "Person")
@DynamicInsert
public static class Person {

    @Id
    private Long id;

    @ColumnDefault("'N/A'")
    private String name;

    @ColumnDefault("-1")
    private Long clientId;

    //Getter and setters omitted for brevity

}
CREATE TABLE Person (
  id BIGINT NOT NULL,
  clientId BIGINT DEFAULT -1,
  name VARCHAR(255) DEFAULT 'N/A',
  PRIMARY KEY (id)
)
In the mapping above, both the name and clientId table columns are going to use a DEFAULT value.
The entity is annotated with the @DynamicInsert annotation so that the INSERT statement does not include the entity attribute that has not been set.
This way, when omitting the name and the clientId attribute, the database is going to set them according to their default values.

Example: @ColumnDefault mapping example

doInJPA( this::entityManagerFactory, entityManager -> {
    Person person = new Person();
    person.setId( 1L );
    entityManager.persist( person );
} );
doInJPA( this::entityManagerFactory, entityManager -> {
    Person person = entityManager.find( Person.class, 1L );
    assertEquals( "N/A", person.getName() );
    assertEquals( Long.valueOf( -1L ), person.getClientId() );
} );
INSERT INTO Person (id) VALUES (?)

6. Columns unique constraint

The @UniqueConstraint annotation is used to specify a unique constraint to be included by the automated schema generator for the primary or secondary table associated with the current annotated entity.
Considering the following entity mapping, Hibernate generates the unique constraint DDL when creating the database schema:
Example: @UniqueConstraint mapping example
@Entity
@Table(
    name = "book",
    uniqueConstraints =  @UniqueConstraint(
        name = "uk_book_title_author",
        columnNames = {
            "title",
            "author_id"
        }
    )
)
public static class Book {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(
        name = "author_id",
        foreignKey = @ForeignKey(name = "fk_book_author_id")
    )
    private Author author;

    //Getter and setters omitted for brevity
}

@Entity
@Table(name = "author")
public static class Author {

    @Id
    @GeneratedValue
    private Long id;

    @Column(name = "first_name")
    private String firstName;

    @Column(name = "last_name")
    private String lastName;

    //Getter and setters omitted for brevity
}
create table author (
    id bigint not null,
    first_name varchar(255),
    last_name varchar(255),
    primary key (id)
)

create table book (
    id bigint not null,
    title varchar(255),
    author_id bigint,
    primary key (id)
)

alter table book
   add constraint uk_book_title_author
   unique (title, author_id)

alter table book
   add constraint fk_book_author_id
   foreign key (author_id)
   references author
With the uk_book_title_author unique constraint in place, it’s no longer possible to add two books with the same title and for the same author.

7. Columns index

The @Index annotation is used by the automated schema generation tool to create a database index.
Considering the following entity mapping, Hibernate generates the index when creating the database schema:

Example: @Index mapping example

@Entity
@Table(
    name = "author",
    indexes =  @Index(
        name = "idx_author_first_last_name",
        columnList = "first_name, last_name",
        unique = false
    )
)
public static class Author {

    @Id
    @GeneratedValue
    private Long id;

    @Column(name = "first_name")
    private String firstName;

    @Column(name = "last_name")
    private String lastName;

    //Getter and setters omitted for brevity
}
create table author (
    id bigint not null,
    first_name varchar(255),
    last_name varchar(255),
    primary key (id)
)

create index idx_author_first_last_name
    on author (first_name, last_name)
Check out - Guide to JPA and Hibernate Cascade Types
Check out - Guide to Hibernate Native SQL Queries
Learn complete hibernate framework at Hibernate Tutorial

GitHub Repository

The complete source code of this article available on my GitHub Repository - https://github.com/RameshMF/Hibernate-ORM-Tutorials

Conclusion

In this article, we have learned below topics with examples:
  1. Generating Schema from Entity Mapping
  2. Importing script files
  3. Database objects
  4. Database-level checks
  5. A default value for a database column
  6. Columns unique constraint
  7. Columns index
You can learn more about Hibernate ORM Framework at Hibernate Tutorial

References

Hibernate ORM 5.4.0.Final User Guide 

Comments