[Spring Boot] Hibernate Naming Strategy
정의
Hibernate 가 entity 클래스 / 필드명 → DB table / column 명으로 변환하는 규칙. 두 단계로 분리:
| 단계 | 역할 | 인터페이스 |
|---|---|---|
| Implicit | entity / field 가 @Table / @Column 명시 없을 때 논리적 이름 생성 (예: User → User) | ImplicitNamingStrategy |
| Physical | 논리적 이름을 실제 DB 식별자 로 변환 (예: User → user, createdAt → created_at) | PhysicalNamingStrategy |
Spring Boot 의 기본:
- Implicit:
SpringImplicitNamingStrategy(entity = 클래스명, column = 필드명) - Physical:
CamelCaseToUnderscoresNamingStrategy(camelCase → snake_case, 모두 소문자)
따라서 User.createdAt → 테이블 user, 컬럼 created_at. 명시적 어노테이션 없어도 모던 SQL 컨벤션 자동 적용.
자동 변환 예
@Entity
class UserAccount {
@Id Long id;
String firstName;
String lastName;
LocalDateTime createdAt;
String OAuthToken;
}
기본 매핑 결과:
CREATE TABLE user_account (
id BIGINT PRIMARY KEY,
first_name VARCHAR(255),
last_name VARCHAR(255),
created_at TIMESTAMP,
o_auth_token VARCHAR(255) -- 주의: 연속 대문자 분해됨
);
연속 대문자 (OAuthToken) 가 o_auth_token 으로 변환되는 점 주의. 의도와 다르면 @Column(name = "oauth_token") 명시.
설정 파일에서 변경
spring:
jpa:
hibernate:
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
| 옵션 | 동작 |
|---|---|
SpringPhysicalNamingStrategy 또는 CamelCaseToUnderscoresNamingStrategy (기본) | camelCase → snake_case |
PhysicalNamingStrategyStandardImpl | 변환 안 함 (논리명 그대로) |
| 커스텀 클래스 | 직접 구현 |
Hibernate 6+ 부터 SpringPhysicalNamingStrategy 가 CamelCaseToUnderscoresNamingStrategy 의 alias 처럼 동작.
legacy DB 호환 (변환 안 함)
기존 DB 가 CreatedAt, UserAccount 처럼 PascalCase 면:
spring:
jpa:
hibernate:
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
이제 entity 의 식별자가 DB 에 그대로 매핑. @Column 어노테이션 거의 불필요해진다.
커스텀 Physical Strategy
특정 패턴 (예: prefix tbl_, 모든 컬럼 lowercase) 적용:
public class PrefixedPhysicalNamingStrategy implements PhysicalNamingStrategy {
@Override
public Identifier toPhysicalCatalogName(Identifier name, JdbcEnvironment ctx) { return name; }
@Override
public Identifier toPhysicalSchemaName(Identifier name, JdbcEnvironment ctx) { return name; }
@Override
public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment ctx) {
if (name == null) return null;
return Identifier.toIdentifier("tbl_" + camelToSnake(name.getText()));
}
@Override
public Identifier toPhysicalSequenceName(Identifier name, JdbcEnvironment ctx) {
return Identifier.toIdentifier("seq_" + camelToSnake(name.getText()));
}
@Override
public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment ctx) {
return Identifier.toIdentifier(camelToSnake(name.getText()).toLowerCase());
}
private String camelToSnake(String s) {
return s.replaceAll("([a-z])([A-Z])", "$1_$2").toLowerCase();
}
}
spring:
jpa:
hibernate:
naming:
physical-strategy: com.example.PrefixedPhysicalNamingStrategy
결과:
User→tbl_userOrderItem→tbl_order_itemcreatedAt→created_at
다른 strategy 비교
SpringPhysicalNamingStrategy (Spring Boot 기본)
UserAccount.firstName → user_account.first_name
가장 흔한 modern SQL convention.
PhysicalNamingStrategyStandardImpl (Hibernate 기본)
UserAccount.firstName → UserAccount.firstName
논리명 그대로. 식별자 그대로 유지하려는 legacy DB 용.
LegacyJpaImpl
@Table(name="UserAccount") // 명시 안 하면 클래스명 그대로
class UserAccount { ... }
JPA 2.0 이전 스타일.
Implicit Naming 의 역할
@JoinColumn 명시 안 했을 때 외래키 컬럼명 생성:
@Entity
class Post {
@ManyToOne
User author; // @JoinColumn 없음
}
SpringImplicitNamingStrategy 가 적용 → author_id 자동 생성. 일반적으로 만족스러움.
@OneToMany(mappedBy="...") 의 join 테이블 명, @ElementCollection 컬렉션 테이블 명 등도 implicit 이 결정.
@Column / @Table 명시 vs 자동
// 자동 변환 의존
@Column
String firstName; // → first_name
// 명시
@Column(name = "first_name")
String firstName; // 동일하지만 명시적
규칙:
- 새 프로젝트: 자동 변환 의존,
@Column생략 - legacy DB / 컬럼명 특수 패턴:
@Column(name=...)명시 - migration 도구 (Flyway, Liquibase) 와 entity 의 컬럼명 일치 강제: 명시 권장
실행 시점에 검증
spring:
jpa:
hibernate:
ddl-auto: validate # entity 매핑 vs DB 스키마 비교, 불일치 시 시작 실패
운영 환경 권장. naming strategy 가 의도와 다르면 시작 시점에 즉시 발견.
DDL 생성 모드별 영향
spring:
jpa:
hibernate:
ddl-auto: create-drop # 시작 시 drop + create, naming strategy 가 컬럼명 결정
properties:
hibernate:
physical_naming_strategy: ...
ddl-auto:
create,create-drop: strategy 가 컬럼명 결정 → DB 에 반영update: 기존 컬럼명 + strategy 둘 다 영향, 충돌 위험validate: strategy 가 만드는 이름이 DB 에 있는지 비교none(운영 권장): strategy 가 SELECT/INSERT 만들 때 컬럼명 사용
함정과 베스트 프랙티스
ddl-auto=update와 strategy 변경 동시 금지: 기존 컬럼명과 새 컬럼명 둘 다 시도하면 schema 가 깨짐- 운영: ddl-auto=validate or none + Flyway/Liquibase: 자동 DDL 위험, migration 도구로 관리
@Column(name=...)와 strategy 의 우선순위: 어노테이션이 항상 이김- 연속 대문자 함정:
OAuthToken→o_auth_token. 의도 안 맞으면 명시 - MySQL 의 case sensitivity 차이: Linux 는 case sensitive, Windows / Mac 은 insensitive. 항상 lowercase 사용 (기본 strategy 가 그렇게 함)
- Postgres reserved word:
user,order,group등은 quote 필요. table name 으로 안 쓰는 게 안전 - strategy 변경은 신중: 기존 entity 들의 매핑 일괄 변경됨
자주 보는 시나리오
시나리오 1: 새 Spring Boot 프로젝트
spring:
jpa:
hibernate:
ddl-auto: create-drop # 개발만
기본 SpringPhysicalNamingStrategy 가 알아서 snake_case. 손 댈 필요 없음.
시나리오 2: legacy Oracle DB (UPPER_CASE)
spring:
jpa:
hibernate:
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
@Entity
@Table(name = "USER_ACCOUNT")
class UserAccount {
@Column(name = "FIRST_NAME") String firstName;
}
명시적 @Table / @Column 사용.
시나리오 3: Multi-tenant prefix
public class TenantPrefixedNamingStrategy implements PhysicalNamingStrategy {
@Override
public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment ctx) {
String tenant = TenantContext.current();
return Identifier.toIdentifier(tenant + "_" + name.getText());
}
// ...
}
tenant 별 별도 테이블 (위험한 패턴, 보통 row-level tenancy 가 낫지만 가능).
관련 위키
이 글의 용어 (5개)
- [JPA] @Embeddable, @Embedded, @Converterspring
- 정의 @Embeddable 은 별도 테이블 없이 entity 의 일부로 포함되는 값 객체 (Value Object). 같은 의미적 그룹의 필드들을 하나의 클래스로 추출. Attr…
- [JPA] PersistenceContext: 1차 캐시, dirty checking, flushspring
- 정의 PersistenceContext 는 가 관리하는 entity 의 메모리 저장소 + 변경 추적기. 트랜잭션 안에서만 살아 있다 ( 진입 시 생성, commit/rollbac…
- [Spring Boot] 자동 구성과 @SpringBootApplicationspring
- 정의 Spring Boot의 핵심 가치 = 자동 구성(autoconfiguration). classpath의 라이브러리를 보고 합리적 기본 설정으로 Bean 등록. Tomcat이…
- [Spring] @Conditional, Profile, @ConfigurationPropertiesspring
- 정의 Spring Boot는 조건부 Bean 등록 메커니즘을 제공한다. classpath, property, 다른 Bean 유무 등에 따라 자동 구성을 활성화/비활성화. , 어노…
- [Spring] Spring Data JPA: Repository, Query Methodsspring
- 정의 Spring Data JPA는 JPA(Hibernate) 위에 Repository 추상화를 얹어 boilerplate를 제거. 인터페이스만 정의하면 Spring이 구현체 자…
💬 댓글