Hibernate and Spring Boot: who is in charge of table names?

When we add a dependency to a project, we sign a contract. Often, many of the terms in it are “written in small print.” In this article, we’ll take a look at something that is easy to overlook when signing a 3-way contract between you, Hibernate and Spring Boot. It’s about naming strategies.

Defaults in JPA

The general rule of thumb for defaults is that they should be intuitive. Let’s check if a typical Spring Boot application with default configuration follows this rule, Hibernate as the JPA implementation and PostgreSQL as the database. Let’s say we have an entity “PetType”. Let’s guess what the name of her table will be in the database.

First example:

@Entity
public class PetType {
    // fields omitted
}

I would assume that the table name would be the class name, i.e. PetType… However, after starting the application, it turned out that the table name is actually pet_type

Let’s explicitly set the name with @Table:

@Entity
@Table(name = "PetType")
public class PetType {
    // fields omitted
}

This time the name should definitely be PetType… Let’s run the application … The table is called again pet_type!

Then let’s try to enclose the table name in quotation marks. Thus, not only the name should be preserved, but also the case:

@Entity
@Table(name = ""PetType"")
public class PetType {
    // fields omitted
}

And again our expectations were not met, the name again "pet_type"but now in quotes!

Hibernate naming strategies

For the query “default table name for JPA entities” Google issues the following result (eng.):

By default, the table name in JPA is the class name (without the package) with a capital letter. Each class attribute is stored in a column of the table.

This is what we expected to see in the first example, isn’t it? Obviously, something is breaking the standard.

Let’s dive deeper into Hibernate. According to documentation, Hibernate has two interfaces responsible for naming tables, columns, etc.: ImplicitNamingStrategy and PhysicalNamingStrategy

ImplicitNamingStrategy responsible for generating names for all objects that were not explicitly named by the developer: entity name, table name, column name, index, foreign key, etc. The resulting name is called boolean, it is used internally by Hibernate to identify the object. This is not the name that will be used in the database.

PhysicalNamingStrategy creates an authentic physical name based on the logical name of the JPA entity. It is the physical name that is used in the database. In fact, this means that using Hibernate you cannot directly specify the physical name of an object in the database, you can only specify the logical one. To better understand how this all works, take a look at the diagram below.

By default, Hibernate uses the following implementations of these interfaces: ImplicitNamingStrategyJpaCompliantImpl and PhysicalNamingStrategyStandardImpl… The former generates logical names according to the JPA specification, while the latter uses them as physical names without any modification. This is best described in the documentation:

JPA defines clear rules for automatic naming. If you are concerned about independence from a particular JPA implementation, or if you want to adhere to the naming conventions defined in JPA, use ImplicitNamingStrategyJpaCompliantImpl (the default strategy). Also, there is no separation in JPA between logical and physical names. According to the JPA specification, a logical name is a physical name. If independence from the JPA implementation is important to you, do not override PhysicalNamingStrategy.

However, our application behaves differently. And that’s why. Spring Boot overrides the default Hibernate implementations for both interfaces and uses instead SpringImplicitNamingStrategy and SpringPhysicalNamingStrategy

SpringImplicitNamingStrategy actually copies the behavior ImplicitNamingStrategyJpaCompliantImpl, there is only a minor difference in naming the join tables. So the point is SpringPhysicalNamingStrategy… IN documentation states the following:

By default, Spring Boot uses SpringPhysicalNamingStrategy as its physical naming strategy. This implementation uses the same naming conventions as Hibernate 4:
1. All dots are replaced with underscores.
2. CamelCase capital letters are converted to lower case and underscores are added between them. In addition, all table names are generated in lower case. For example, the TelephoneNumber entity corresponds to a table named telephone_number.

Basically, Spring Boot always converts camelCase and PascalCase to snake_case. Moreover, using anything other than snake_case is not possible at all. I wouldn’t use camelCase or PascalCase for naming database objects, but sometimes we have no choice. If your Spring Boot application is running a third-party database that uses PascalCase or camelCase, the default Spring Boot configuration will not work for you. Be sure to make sure the PhysicalNamingStrategy you are using is compatible with the names in the database.

It turns out that Hibernate is JPA-compliant, but Spring Boot is not. You might think this is a mistake. However, Spring Boot is a framework in which many decisions have already been taken for the developer, and this is its value. In other words, he has every right to implement in his own way the standards and specifications of the technologies that he uses. For the developer, this means the following:

  • The final behavior is always implementation-defined and may differ from the specification.

  • If something works out of the box, you always need to figure out what exactly it does under the hood.

  • The default behavior can change with an update to the library version, which can lead to unpredictable side effects;

Conclusion

The magic of out-of-the-box configurations is great, but can lead to unexpected behavior. One way to avoid these problems is to explicitly set all values ​​and not rely on autogeneration. For naming JPA objects, the guidelines are as follows:

  1. Always name your JPA objects explicitly so that no automatic naming strategy affects your code.

  2. Use snake_case for column, table, index, and other JPA object names so that all PhysicalNamingStrategy implementations do not convert them in any way.

  3. If snake_case cannot be used (for example, when using a third party database), use PhysicalNamingStrategyStandardImpl as PhysicalNamingStrategy.

Another benefit of explicitly naming JPA objects is that you never accidentally rename a table or attribute in the base itself when refactoring a Java model. To do this, you will have to change the name in @Table or @Column

However, by introducing these rules, we simply shifted the responsibility to the developers. Now we need to make sure that the whole team follows them. Fortunately, this process can be automated using development tools.

If you are using IntelliJ IDEA try Jpa buddy Is a plugin that makes it easy to work with JPA, Hibernate, Spring Data JPA, Liquibase and similar technologies. There is a special section in JPA Buddy settings where you can set templates for entity names, attributes, etc. These templates are applied every time developers create an entity or attribute:

By design, JPA Buddy settings are stored in Git, so all developers use the same templates and follow the same rules.

Similar Posts

Leave a Reply

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