skip to content

Naming Strategies

How Hibernate turns CamelCaseProperty into a physical column name and how to override it globally. Comes up in interviews as the explanation for mysterious snake_case columns and quoted-identifier bugs.

part ofHibernateoverview, primer and where to startread it →
on this pageshow

questions

4

In Hibernate, what is the difference between an ImplicitNamingStrategy and a PhysicalNamingStrategy, and when does each one run while the mapping model is built?

level: middleimportance: must knowfreq 45%

answer

  1. implicit = name when unnamed
  2. physical = every name to identifier
  3. annotated names skip implicit, not physical
  4. JPA-compliant default = property name verbatim
  5. two boot settings: implicit_/physical_naming_strategy

basics

~20 s

ImplicitNamingStrategy invents a logical name when the mapping does not state one (entity name, field name, join-table name). PhysicalNamingStrategy then turns every logical name - invented or explicitly annotated - into the actual database identifier Hibernate emits in SQL.

solid answer

~50 s

Hibernate names things in two passes. First it settles a logical name for every table, column, sequence, join table and constraint. If the mapping supplies one (@Table(name=...), @Column(name=...)), that is the logical name; if not, the ImplicitNamingStrategy derives it - the JPA-compliant default gives the unqualified entity name for tables and the property name for columns. Second, every logical name - derived or annotated - passes through the PhysicalNamingStrategy, which produces the identifier Hibernate actually writes into DDL and SQL. The shipped default is a pass-through. So implicit answers 'what do we call it when nobody said', physical answers 'how does any name become a database identifier'. That is why global conventions - camelCase to snake_case, a fixed prefix, length truncation - belong in the physical strategy: it is the only hook that also sees explicitly annotated names. Configure them with hibernate.implicit_naming_strategy and hibernate.physical_naming_strategy.

code

java · 7 lines
java
public class PrefixingPhysicalNamingStrategy extends PhysicalNamingStrategyStandardImpl {
    @Override
    public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment env) {
        if (name == null) return null;
        return Identifier.toIdentifier("app_" + name.getText(), name.isQuoted());
    }
}

go deeper

for a junior

Be able to say which hook names things you left unnamed and which one rewrites all names, and know the two configuration property names.

for a middle

Explain the two-pass model precisely, including that annotated names still go through the physical strategy, and pick the right hook for a given convention.

for a senior

Discuss consequences: schema migration when a strategy changes, quoting preservation in custom implementations, and constraint naming living in the implicit hook.

for a principal

Frame it as governance - one boot-time strategy enforcing house conventions across many services beats per-entity annotations, and settle who owns identifier rules between developers and DBAs.

## Two different questions Turning Java identifiers into database identifiers hides two independent questions. 1. What is this thing called if nobody said? An entity class CustomerOrder with field createdAt and no name attributes still needs a table and a column name. 2. How is any chosen name spelled as a database identifier? Maybe every table gets a prefix, or camelCase must become snake_case, or the identifier must be truncated to 30 characters. Hibernate 5 split the old single NamingStrategy interface into exactly these two hooks, and Hibernate 6 keeps them. ## Pass 1: logical names and ImplicitNamingStrategy While reading annotations Hibernate builds a mapping model in which every table, column, sequence, join table, foreign key, unique key and index has a logical name. If the annotation carries a name, that string is the logical name. If it does not, Hibernate calls the ImplicitNamingStrategy: determinePrimaryTableName, determineBasicColumnName, determineJoinTableName, determineForeignKeyName and so on. The default is ImplicitNamingStrategyJpaCompliantImpl, which follows the JPA spec: table name = unqualified entity name; basic column = property name verbatim; a @ManyToOne join column = property name + underscore + referenced primary key column; a join table = owner table + underscore + other table. Nothing here lower-cases or de-camel-cases anything: field createdAt yields logical column createdAt. Other shipped variants exist (legacy-jpa, legacy-hbm, component-path, which prefixes embedded attributes with the component path) and you can supply your own class. ## Pass 2: physical names and PhysicalNamingStrategy Once the mapping model is complete, each logical name is handed to the PhysicalNamingStrategy: toPhysicalTableName, toPhysicalColumnName, toPhysicalSequenceName, toPhysicalSchemaName, toPhysicalCatalogName. Each receives an Identifier plus the JdbcEnvironment (dialect, identifier helper) and returns the Identifier to actually use. The crucial property is that this pass sees every name, including the ones you wrote by hand in @Column. The default PhysicalNamingStrategyStandardImpl returns the name unchanged, which is why out of the box the physical name equals the logical name and people forget the second pass exists. ## Why the distinction matters in practice When you want a house convention across the whole schema, you override the physical strategy: it is uniform, it also normalises hand-written names, and it cannot be forgotten on one entity. When you want to change only defaults - a naming rule for join tables or for foreign key constraints - you override the implicit strategy, because those names are only ever produced there. A second consequence: logical names are what Hibernate uses internally to resolve references across the mapping (for example @JoinColumn(name=...) refers to a logical column name), while physical names are what appears in SQL. JPQL and HQL are written against entity and attribute names, so neither strategy touches your queries - only the emitted SQL and DDL change. ## Configuration Both are boot-time settings: hibernate.implicit_naming_strategy (short names default, jpa, legacy-jpa, legacy-hbm, component-path, or an FQN) and hibernate.physical_naming_strategy (an FQN or instance). Because they run at bootstrap, changing them changes generated DDL and every emitted statement; against an existing schema you must migrate the schema to match.

  • Which hook do you override to make the whole schema snake_case, and why is the other one the wrong place?
    The physical strategy, because it is applied to every logical name including the ones written explicitly in @Table and @Column, so the convention holds uniformly. An implicit strategy would only affect entities and fields that left the name out; the moment someone adds an explicit @Column the rule silently stops applying.
  • Does the implicit strategy also name things other than tables and columns?
    Yes. It has methods for join tables, join columns, collection tables, discriminator and tenant-id columns, and for foreign key, unique key and index constraint names. Those names are only ever produced implicitly unless you annotate them, so a house convention for constraint names has to live in the implicit strategy.
  • Do these strategies affect how you write JPQL?
    No. JPQL and HQL are written against entity names and attribute names, which come from the Java model, not from the physical identifiers. Naming strategies only change generated DDL and the SQL Hibernate emits, so switching strategy never requires editing a JPQL query - only migrating the database schema.

Implicit naming is the clerk who fills in a blank form field for you; physical naming is the printer that stamps every field - blank-filled or hand-written - into the house typeface.

saying these in an interview costs you the question

  • Saying an explicitly annotated @Column name bypasses both strategies - it bypasses only the implicit one
  • Claiming Hibernate converts camelCase to snake_case by default - the JPA-compliant default copies the property name verbatim
  • Thinking the implicit strategy is the place to implement a global prefix or case convention
  • Believing a naming strategy rewrites your JPQL identifiers as well as the SQL
  • Assuming the strategies can be swapped at runtime per request - they are boot-time settings baked into the mapping model

context

open as a page

A Hibernate entity field named createdAt is mapped to a column literally called createdAt, but the database convention is created_at. How do you make Hibernate map camelCase Java names to snake_case identifiers across the whole schema?

level: juniorimportance: should knowfreq 40%

basics

~10 s

Set hibernate.physical_naming_strategy to a snake-casing implementation - Hibernate ships CamelCaseToUnderscoresNamingStrategy. It rewrites every table, column and sequence name, including ones written explicitly in annotations, so you do not annotate each field.

open as a page

An entity declares @Table(name = "USER_ACCOUNT") and @Column(name = "emailAddress"). Does a configured Hibernate PhysicalNamingStrategy still get a say in those names? What about the ImplicitNamingStrategy?

level: middleimportance: should knowfreq 25%

basics

~20 s

The implicit strategy is skipped - a name was given. The physical strategy still runs: the annotated string is the logical name, and every logical name is converted by the physical pass, so it can be rewritten.

open as a page

One of your Hibernate entities maps to a table called order and has a column called user, both reserved words in the database. What options does Hibernate give you to make the generated SQL valid, and what is the risk of switching on the setting hibernate.globally_quoted_identifiers?

level: seniorimportance: should knowfreq 30%

basics

~10 s

Quote the offending names individually - backticks or escaped double quotes in @Table/@Column - or enable hibernate.auto_quote_keyword. hibernate.globally_quoted_identifiers quotes everything, which makes all identifiers case-sensitive and locks the schema to their exact spelling.

open as a page