Naming Strategies in Hibernate 5

By Thorben Janssen

Configuration, Mapping

JPA and Hibernate provide a default mapping that maps each entity class to a database table with the same name. Each of its attributes gets mapped to a column with the same. But what if you want to change this default, e.g., because it doesn’t match your company’s naming conventions?

You can, of course, specify the table name for each entity and the column name for each attribute. That requires a @Table annotation on each class and a @Column annotation on each attribute. This is called an explicit naming.

That’s a good approach if you want to change the mapping for one attribute. But doing that for lots of attributes requires a lot of work. Adapting Hibernate’s naming strategy is then often a better approach.

In this article, I will show you how to use it to adjust the mapping of all entities and attributes. But before we do that, we first need to talk about the difference between Hibernate’s logical and physical naming strategy.

A 2-step approach

Hibernate splits the mapping of the entity or attribute name to the table or column name into 2 steps:

  1. It first determines the logical name of an entity or attribute. You can explicitly set the logical name using the @Table and @Column annotations. If you don’t do that, Hibernate will use one of its implicit naming strategies.
  2. It then maps the logical name to a physical name. By default, Hibernate uses the logical name as the physical name. But you can also a PhysicalNamingStrategy that maps the logical name to a physical one that follows your internal naming convention.

So, why does Hibernate differentiate between a logical and a physical naming strategy, but the JPA specification doesn’t?

JPA’s approach works, but if you take a closer look at it, you recognize that Hibernate’s approach provides more flexibility. By splitting the process into 2 steps, Hibernate allows you to implement a conversion that gets applied to all attributes and classes.

If your naming conventions, for example, require you to ad “_TBL” to all table names, you can do that in your PhysicalNamingStrategy. It then doesn’t matter if you explicitly specify the table name in a @Table annotation or if you do it implicitly based on the entity name. In both cases, Hibernate will add “_TBL” to the end of your table name.

Because of the added flexibility, I like Hibernate’s approach a little better.

Logical naming strategy

As explained earlier, you can either define the logical name explicitly or implicitly. Let’s take a look at both options.

Explicit naming strategy

The explicit naming strategy is very easy to use. You probably already used it yourself. The only thing you need to do is to annotate your entity class with @Table or your entity attribute with @Column and provide your preferred name as a value to the name attribute.

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

    @Column(name = "author_name")
    private String name;

    ...
}

If you then use this entity in your code and activate the logging of SQL statements, you can see that Hibernate uses the provided names instead of the default ones.

15:55:52,525 DEBUG [org.hibernate.SQL] - insert into AUTHORS (author_name, version, id) values (?, ?, ?)

Implicit naming strategy

If you don’t set the table or column name in an annotation, Hibernate uses one of its implicit naming strategies. You can choose between 4 different naming strategies and 1 default strategy:

  • default
    By default, Hibernate uses the implicit naming strategy defined by the JPA specification. This value is an alias for jpa.
  • jpa
    This is the naming strategy defined by the JPA 2.0 specification.
    The logical name of an entity class is either the name provided in the @Entity annotation or the unqualified class name. For basic attributes, it uses the name of the attributes as the logical name. To get the logical name of a join column of an association, this strategy concatenates the name of the referencing attribute, an “_” and the name of the primary key attribute of the referenced entity. The logical name of a join column of an element collection consists of the name of the entity that owns the association, an “_” and the name of the primary key attribute of the referenced entity. And the logical name of a join table starts with the physical name of the owning table, followed by an “_” and the physical name of the referencing table.
  • legacy-hbm
    This is Hibernate’s original naming strategy. It doesn’t recognize any of JPA’s annotations. But you can use Hibernate’s proprietary configuration file and annotations to define a column or entity name.
    In addition to that, there are a few other differences to the JPA specification:
    • The logical name of a join column is only its attribute name.
    • For join tables, this strategy concatenates the name of the physical table that owns the association, an “_” and the name of the attribute that owns the association.
  • legacy-jpa
    The legacy-jpa strategy implements the naming strategy defined by JPA 1.0.
    The main differences to the jpa strategy are:
    • The logical name of a join table consists of the physical table name of the owning side of the association, an “_” and either the physical name of the referencing side of the association or the owning attribute of the association.
    • To get the logical name of the join column of an element collection, the legacy-jpa strategy uses the physical table name instead of the entity name of the referenced side of the association. That means the logical name of the join column consists of the physical table name of the referenced side of the association, an “_” and the name of the referenced primary key column.
  • component-path
    This strategy is almost identical to the jpa strategy. The only difference is that it includes the name of the composite in the logical attribute name.

You can configure the logical naming strategy by setting the hibernate.implicit_naming_strategy attribute in your configuration.

<persistence>
    <persistence-unit name="naming">
        ...
        <properties>
            <property name="hibernate.implicit_naming_strategy"
                      value="jpa" />
            ...
        </properties>
    </persistence-unit>
</persistence>

Physical naming strategy

Implementing your own physical naming strategy isn’t complicated. You can either implement the PhysicalNamingStrategy interface or extend Hibernate’s PhysicalNamingStrategyStandardImpl class.

I prefer extending Hibernate’s PhysicalNamingStrategyStandardImpl. In the following examples, to create a naming strategy that adds the postfix “_TBL” to each table name and to create a naming strategy that converts camel case names into snake case.

Table postfix strategy

The only thing I want to change in this naming strategy is the handing of the table name. So, extending Hibernate’s PhysicalNamingStrategyStandardImpl class it the easiest way to achieve that.

Implementing a custom strategy

I overwrite the toPhysicalTableName method, add a static postfix to the name, and convert it into an Identifier.

public class TablePostfixPhysicalNamingStrategy extends PhysicalNamingStrategyStandardImpl {

    private final static String POSTFIX = "_TBL";
    
    @Override
    public Identifier toPhysicalTableName(final Identifier identifier, final JdbcEnvironment jdbcEnv) {
        if (identifier == null) {
            return null;
        }

        final String newName = identifier.getText() + POSTFIX;
        return Identifier.toIdentifier(newName);
    }

}

In the next step, you need to activate the naming strategy. You do that by setting the hibernate.physical_naming_strategy attribute to the fully qualified class name of the strategy.

<persistence>
    <persistence-unit name="naming">
        ...
        <properties>
            <property name="hibernate.physical_naming_strategy"
                      value="org.thoughtsonjava.naming.config.TablePostfixPhysicalNamingStrategy" />
            ...
        </properties>
    </persistence-unit>
</persistence>

Using the table postfix strategy

Let’s try this mapping using this basic Author entity. I don’t specify a logical name for the entity. So, it defaults to the name of the class, which is Author. Without our custom naming strategy, Hibernate would map this entity to the Author table.

@Entity
public class Author {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Version
    private int version;

    private String name;

    @ManyToMany(mappedBy = "authors", fetch = FetchType.LAZY)
    private Set<Book> books;

    ...
}

When I persist this entity, you can see in the log file that Hibernate mapped it to the AUTHOR_TBL table.

14:05:56,619 DEBUG [org.hibernate.SQL] - insert into Author_TBL (name, version, id) values (?, ?, ?)

Names in snake case instead of camel case

In Java, we prefer to use camel case for our class and attribute names. By default, Hibernate uses the logical name as the physical name. So, the entity attribute LocalDate publishingDate gets mapped to the database column publishingDate.

Some companies use naming conventions that require you to use snake case for your table and column names. That means that your publishingDate attribute needs to be mapped to the publishing_date column.

As explained earlier, you could use the explicit naming strategy and annotate each attribute with a @Column annotation. But for most persistence layers, that’s a lot of work, and it’s easy to forget.

So, let’s implement a naming strategy that does that for us.

Implementing a custom strategy

public class SnakeCasePhysicalNamingStrategy extends PhysicalNamingStrategyStandardImpl {

    @Override
    public Identifier toPhysicalCatalogName(Identifier name, JdbcEnvironment context) {
        return super.toPhysicalCatalogName(toSnakeCase(name), context);
    }

    @Override
    public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
        return super.toPhysicalColumnName(toSnakeCase(name), context);
    }

    @Override
    public Identifier toPhysicalSchemaName(Identifier name, JdbcEnvironment context) {
        return super.toPhysicalSchemaName(toSnakeCase(name), context);
    }

    @Override
    public Identifier toPhysicalSequenceName(Identifier name, JdbcEnvironment context) {
        return super.toPhysicalSequenceName(toSnakeCase(name), context);
    }

    @Override
    public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
        return super.toPhysicalTableName(toSnakeCase(name), context);
    }
    
    private Identifier toSnakeCase(Identifier id) {
        if (id == null)
            return id;
            
        String name = id.getText();
        String snakeName = name.replaceAll("([a-z]+)([A-Z]+)", "$1\\_$2").toLowerCase();
        if (!snakeName.equals(name))
            return new Identifier(snakeName, id.isQuoted());
        else
            return id;
    }
}

The interesting part of this naming strategy is the toSnakeCase method. I call it in all methods that return a physical name to convert the provided name to snake case.

If you’re familiar with regular expressions, the implementation of the toSnakeCase method is pretty simple. By calling replaceAll(“([a-z]+)([A-Z]+)”, “$1\\_$2”), we add an “_” in front of each capital letter. After that is done, we only need to change all characters to lower case.

In the next step, we need to set the strategy in the persistence.xml file.

<persistence>
    <persistence-unit name="naming">
        ...
        <properties>
            <property name="hibernate.physical_naming_strategy"
                      value="org.thoughtsonjava.naming.config.SnakeCasePhysicalNamingStrategy" />
            ...
        </properties>
    </persistence-unit>
</persistence>

Using the snake case strategy

When I now persist this Book entity, Hibernate will use the custom strategy to map the publishingDate attribute to the database column publishing_date.

@Entity
public class Book {

    @Id
    @GeneratedValue
    private Long id;

    @Version
    private int version;

    private String title;

    private LocalDate publishingDate;

    @ManyToMany
    private Set<Author> authors;

    @ManyToOne
    private Publisher publisher;

    ...
}

As you can see in the log file, the naming strategy worked as expected and changed the name of the publishingDate column to publishing_date.

14:28:59,337 DEBUG [org.hibernate.SQL] - insert into books (publisher_id, publishing_date, title, version, id) values (?, ?, ?, ?, ?)

Conclusion

Hibernate’s naming strategy provides you with lots of flexibility. It consists of 2 parts, the mapping of the logical and the physical name.

You can explicitly define the logical name using the @Table and @Column annotation. If you don’t do that, Hibernate uses one of its implicit naming strategies. The default one is compliant with JPA 2.0.

After the logical name got determined, Hibernate applies a physical naming strategy. By default, it returns the logical name. But you can use it to implement a conversion that gets applied to all logical names. As you have seen in the examples, this provides an easy way to fulfill your internal naming conventions.


Tags

Configuration, Mapping


About the author

Thorben is an independent consultant, international speaker, and trainer specialized in solving Java persistence problems with JPA and Hibernate.
He is also the author of Amazon’s bestselling book Hibernate Tips - More than 70 solutions to common Hibernate problems.

Books and Courses

Coaching and Consulting

Leave a Repl​​​​​y

Your email address will not be published. Required fields are marked

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  1. Thank you for every time you release an article.
    Your website Thoughts on Java is very helpful and valuable.
    you always give us information for free.
    For more this course help all of you a lot.

  2. Really useful, i have been in the pain to use @Column(name = “name”) for all the attributes. Thanks for sharing the knowledge.

{"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}