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 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.
Hibernate splits the mapping of the entity or attribute name to the table or column name into 2 steps:
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.
As explained earlier, you can either define the logical name explicitly or implicitly. Let’s take a look at both options.
The explicit naming strategy is very easy to use. You probably already used it yourself. The only thing you need to do is 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 (?, ?, ?)
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:
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>
As mentioned earlier, Hibernate’s default physical naming strategy uses the logical name without changing it. So, whatever the logic name is, it will also be the name of the database table or column.
If you prefer a different mapping, you can define a custom strategy. I will show you how to do that later in this article. But before that, I want to show you the CamelCaseToUnderscoresNamingStrategy that was introduced in Hibernate 5.5.4. It replicates the mapping used by Spring Boot’s SpringPhysicalNamingStrategy.
Spring’s SpringPhysicalNamingStrategy has become very popular. It not only gets used by default in Spring Boot applications, but many developers also started to apply it to non-Spring projects. It replaces all dots and camel casing with underscores and generates all tables names in lower case.
The Hibernate team replicated that mapping strategy in the CamelCaseToUnderscoresNamingStrategy. Since Hibernate 5.5.4.Final, you can activate this strategy by setting the configuration property hibernate.physical_naming_strategy in your persistence.xml file to org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy.
<persistence>
<persistence-unit name="my-persistence-unit">
...
<properties>
...
<property name="hibernate.physical_naming_strategy"
value="org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy"/>
</properties>
</persistence-unit>
</persistence>
We need an entity class name in camel case to show this strategy in all details. Because of that, I’m using the ChessPlayer entity instead of the Author entity you saw in previous examples.
@Entity
public class ChessPlayer {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "player_seq")
@SequenceGenerator(name = "player_seq", sequenceName = "player_seq", initialValue = 100)
private Long id;
private String firstName;
private String lastName;
private LocalDate birthDate;
@OneToMany(mappedBy = "playerWhite")
private Set<ChessGame> gamesWhite;
@OneToMany(mappedBy = "playerBlack")
private Set<ChessGame> gamesBlack;
@Version
private int version;
...
}
As you can see in the code snippet, I don’t define any logical names for the entity class and its attributes. By default, Hibernate then uses the Java class’s name and the names of each of its attributes as their logical names.
Hibernate’s CamelCaseToUnderscoresNamingStrategy physical naming strategy replaces all dots and camel casing with underscores and changes the logical class name to lower case. Based on this mapping, the ChessGame entity class gets mapped to the chess_game table. And the attributes firstName, lastName, and birthDate get mapped to the columns first_name, last_name, and birth_date.
You can see that when I persist a new ChessGame entity object.
19:27:25,995 DEBUG SQL:144 - select chessplaye0_.id as id1_1_0_, chessplaye0_.birth_date as birth_da2_1_0_, chessplaye0_.first_name as first_na3_1_0_, chessplaye0_.last_name as last_nam4_1_0_, chessplaye0_.version as version5_1_0_ from chess_player chessplaye0_ where chessplaye0_.id=?
If none of Hibernate’s physical naming strategies fulfill your requirements, you can implement your own strategy. Doing that isn’t complicated. You can either implement the PhysicalNamingStrategy interface or extend Hibernate’s PhysicalNamingStrategyStandardImpl class.
I extend Hibernate’s PhysicalNamingStrategyStandardImpl in the following example to create a naming strategy that adds the postfix “_TBL” to each table name And in the 2nd example, we will define a naming strategy that converts camel case names into snake case.
The only thing I want to change in this naming strategy is the handing of the table name. Extending Hibernate’s PhysicalNamingStrategyStandardImpl class is the easiest way to achieve that.
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>
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 (?, ?, ?)
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.
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>
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 (?, ?, ?, ?, ?)
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. Since version 5.5.4, Hibernate also offers the CamelCaseToUnderscoresNamingStrategy. It replaces all dots and camel casing with underscores and generates all tables names in lower case. And you can also implement your own physical naming strategy. Most teams use this to apply a naming convention to all logical entity and attribute names. As you have seen in the examples, this provides an easy way to fulfill your internal naming conventions.