skip to content

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%

answer

  1. backticks = portable quote marker
  2. quoted implies case-sensitive
  3. auto_quote_keyword = dialect keyword list, not exhaustive
  4. globally_quoted = everything, breaking on live schemas
  5. custom strategy must preserve isQuoted

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.

solid answer

~50 s

Three options, in increasing bluntness. 1. Per-identifier quoting: @Table(name = "`order`") with backticks, or JPA-standard escaped double quotes. Hibernate translates the marker into the dialect's quote character, so the same mapping works on different databases. Surgical and my default. 2. hibernate.auto_quote_keyword: Hibernate quotes identifiers it recognises as keywords for the dialect while building the mapping. Convenient, but the keyword list is not identical to every database's, so it is a helper rather than a guarantee. 3. hibernate.globally_quoted_identifiers: every table, column and sequence is quoted. The global option is where people get hurt. Quoted identifiers are case-sensitive on most engines - on PostgreSQL, quoting "createdAt" makes it a genuinely mixed-case column, no longer the folded createdat, so hand-written SQL, views and migration scripts must quote it identically forever. It also makes the schema harder to type by hand. I use it only when the whole schema is generated and owned by Hibernate.

code

java · 6 lines
java
@Entity
@Table(name = "`order`")
public class Order {
    @Id Long id;
    @Column(name = "`user`") String user;
}

go deeper

for a junior

Know that reserved names must be quoted and that backticks in the annotation are the portable way to ask for it.

for a middle

Contrast per-identifier quoting, auto keyword quoting and the global switch, and state what quoting does to case sensitivity.

for a senior

Lead with renaming where possible, and discuss the blast radius of global quoting on migrations, views and other consumers of the schema.

for a principal

Set the policy: reserved words banned by convention in new schemas, quoting confined to legacy identifiers, ownership of the schema spelling made explicit.

## Why reserved words break SQL engines reserve words such as order, user, group, table and select. If Hibernate emits `select o.id from order o`, the parser sees the ORDER keyword and rejects the statement. The fix is always the same in principle - wrap the identifier in the database's quoting characters (double quotes in standard SQL and PostgreSQL, backticks in MySQL, square brackets in SQL Server) - but Hibernate must know which identifiers need it, and must translate to the right character per dialect. ## Per-identifier quoting The portable way is to mark the name in the mapping. Hibernate accepts backticks around a name in any name attribute: @Table(name = "`order`"), @Column(name = "`user`"), also inside @JoinColumn and @JoinTable. JPA's own convention is a name that starts and ends with an escaped double quote. Either way Hibernate stores the Identifier with quoted = true and the dialect renders it with the right characters. This is the surgical fix and it keeps the rest of the schema unquoted and case-folded as usual. A subtlety: once an identifier is quoted, it is exact. On PostgreSQL an unquoted identifier folds to lower case, so order quoted as "order" is fine, but "Order" would be a different column from order. Keep quoted names lower case unless the existing schema says otherwise. ## Automatic keyword quoting Hibernate offers hibernate.auto_quote_keyword, which asks it to quote names it recognises as reserved for the configured dialect while building the mapping model. It saves you from auditing every entity, and it is particularly useful for schema tooling. The caveat is coverage: the keyword set comes from the dialect and JDBC metadata, and databases add reserved words between versions, so a name can slip through. Treat it as a safety net, not a policy. ## Global quoting and its cost hibernate.globally_quoted_identifiers = true makes Hibernate quote every table, column, sequence, schema and catalog name it emits. It certainly solves reserved words. The price: - Case sensitivity everywhere. Every identifier now means exactly what it is spelled as. If your mapping produces createdAt and the table has created_at or createdat, statements fail where they previously worked through the engine's case folding. Retrofitting this onto a live schema is a breaking change. - Coupling to the exact spelling. Views, stored procedures, reporting queries and migration scripts written by other teams must quote identically. - Ergonomics. Hand-written SQL against the schema becomes tedious, and mistakes are silent until runtime. - A companion setting, hibernate.globally_quoted_identifiers_skip_column_definitions, exists because quoting inside generated column definitions broke some databases - a hint that the global switch has rough edges. ## How this interacts with naming strategies Quoting travels with the Identifier through the physical naming pass. A custom PhysicalNamingStrategy that rebuilds identifiers with Identifier.toIdentifier(text) and forgets the quoted flag will silently strip quoting and reintroduce the reserved-word failure; always pass name.isQuoted() through. Conversely, a strategy can be the place to implement the policy - for example, quoting any name that appears in a curated keyword list. ## What I would do Prefer renaming: a table called orders instead of order removes the problem for everyone, including hand-written SQL. Where the name is fixed by a legacy schema, quote that identifier explicitly. Reserve the global switch for schemas that Hibernate alone generates and owns.

  • Why does enabling global quoting sometimes break an application that worked fine against an existing schema?
    Unquoted identifiers are case-folded by the engine - lower case on PostgreSQL, upper case on Oracle - so a mapping name that differed only in case still resolved. Quoting removes the folding, so the mapping name must match the stored identifier exactly, and every mismatch becomes a runtime error.
  • You wrote a custom PhysicalNamingStrategy and suddenly the reserved-word errors came back. What is the likely cause?
    The strategy rebuilt identifiers without carrying the quoted flag, for example Identifier.toIdentifier(newText) instead of Identifier.toIdentifier(newText, name.isQuoted()). Hibernate then renders the name unquoted and the reserved word reaches the parser. Always propagate isQuoted, and usually return quoted names untouched.

saying these in an interview costs you the question

  • Turning on hibernate.globally_quoted_identifiers as the default fix without mentioning case sensitivity
  • Hardcoding double quotes or backticks into the name instead of using the portable marker
  • Claiming hibernate.auto_quote_keyword covers every reserved word in every database version
  • Forgetting that quoting must also be honoured by views, migrations and hand-written SQL
  • Dropping the quoted flag inside a custom physical naming strategy

context