How do you customize naming globally in Spring Data JDBC, and which NamingStrategy methods matter beyond table and column names?
answer
- One NamingStrategy @Bean = global
- getSchema / getTableName / getColumnName
- getReverseColumnName = child back-ref FK
- getKeyColumn = List index / Map key
- @Table/@Column/@MappedCollection override it
basics
~10 sRegister a NamingStrategy bean and override its methods. Beyond getTableName and getColumnName, you can override getSchema (schema prefix), getReverseColumnName (back-reference FK in child tables) and getKeyColumn (list index / map key column).
solid answer
~40 sTo change naming globally, expose a single `NamingStrategy` bean; Spring Data JDBC's mapping context uses it for all SQL generation. The key overridable methods are: `getTableName(Class)` and `getColumnName(RelationalPersistentProperty)` for the obvious cases; `getSchema()` to prefix a schema on every qualified table name; `getReverseColumnName(...)` which names the **back-reference** foreign-key column in a child table of a `@MappedCollection` relationship (default is the parent property/table name); and `getKeyColumn(...)` which names the extra column storing a List index or Map key. Per-element annotations (`@Table`, `@Column`, `@MappedCollection(idColumn=..., keyColumn=...)`) always win over the strategy. A common recipe is a strategy that lower-snake-cases getTableName/getColumnName to mimic JPA, optionally pluralizing table names. Embedded prefixes and AggregateReference columns flow through the same strategy.
code
java · 27 linesimport org.springframework.data.relational.core.mapping.NamingStrategy;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
class NamingConfig {
@Bean
NamingStrategy snakeCaseStrategy() {
return new NamingStrategy() {
@Override public String getSchema() { return "app"; }
@Override public String getTableName(Class<?> type) {
return snake(type.getSimpleName());
}
@Override public String getColumnName(RelationalPersistentProperty p) {
return snake(p.getName());
}
@Override public String getReverseColumnName(RelationalPersistentProperty p) {
// FK column in child table pointing back to the parent
return snake(p.getOwner().getType().getSimpleName());
}
private String snake(String s) {
return s.replaceAll("([a-z0-9])([A-Z])", "$1_$2").toLowerCase();
}
};
}
}go deeper
Know a NamingStrategy bean changes naming globally.
Override getTableName/getColumnName and understand @Table/@Column precedence.
Also handle getReverseColumnName and getKeyColumn for collections, and know the interaction with embedded/AggregateReference.
Decide global strategy vs explicit annotations, keep runtime names consistent with hand-written migrations, and manage schema placement.
## The global extension point Spring Data JDBC's `RelationalMappingContext` is configured with a `NamingStrategy`. If you declare a `NamingStrategy` `@Bean`, Boot's autoconfiguration wires it in, so every generated SQL identifier passes through your logic. You typically implement the interface anonymously or as a class and override only the methods you care about (all have verbatim defaults). ## Methods that matter 1. `getSchema()` — returns a schema name applied to qualified table names. Default `""` (none). Override to force everything into, say, `app`. 2. `getTableName(Class<?> type)` — table for an aggregate root / entity. Default: simple class name. Snake-case + pluralize here for JPA-like naming. 3. `getColumnName(RelationalPersistentProperty property)` — column for a scalar/simple property, an `@Embedded` sub-property (before prefix), and an `AggregateReference` column. Default: property name. 4. `getReverseColumnName(...)` — the **back-reference** column in a child table when a parent has a `@MappedCollection` (one-to-many) or single nested entity. This is the FK pointing back to the parent. Default derives from the parent property/table. Get this wrong and one-to-many mappings look for the wrong FK column. 5. `getKeyColumn(...)` — for `List<T>` an extra integer column stores the list index; for `Map<K,V>` a column stores the key. This method names that column. Default derives from the property name plus `_key`. 6. `getQualifiedTableName(...)` — combines schema + table; usually you override `getSchema`/`getTableName` and leave this. ## Precedence Explicit annotations override the strategy for that element: - `@Table(name=..., schema=...)` on a class. - `@Column(name=...)` on a property. - `@MappedCollection(idColumn=..., keyColumn=...)` names the back-reference and key columns for a specific relationship, overriding `getReverseColumnName`/`getKeyColumn`. ## Interactions - `@Embedded` sub-columns: base name from `getColumnName`, then the embedded `prefix` is concatenated. - `AggregateReference` columns: named by `getColumnName` on the reference property. - A global snake_case strategy therefore snake-cases embedded and reference columns too — usually desirable for consistency. ## Gotchas - Overriding only `getColumnName` but not `getReverseColumnName` leaves child-table FK columns in the old convention, breaking one-to-many mapping — override both for consistency. - Pluralizing table names naively (append `s`) breaks irregular nouns; keep it predictable or use `@Table` for exceptions. - There must be exactly one `NamingStrategy` bean; multiple candidates cause ambiguity. - The strategy affects generated DDL only if you generate it from the model; if you hand-write Liquibase migrations, the column names in SQL must match what the strategy produces at runtime.
- You snake-cased getColumnName but a one-to-many mapping still can't find its FK column. Why?The child table's back-reference column is produced by getReverseColumnName, not getColumnName. You must override it too (or set @MappedCollection(idColumn=...)) so the parent FK column name matches your convention.
saying these in an interview costs you the question
- Thinking getColumnName covers back-reference FK columns in child tables
- Assuming spring.jpa.* naming properties affect Spring Data JDBC
- Registering multiple NamingStrategy beans without qualifiers