본문 바로가기
study/JPA

[Hibernate] 하이버네이트 명명 전략(Hibernate Naming Strategy)

by eo_neunal 2022. 9. 22.

0. 들어가며

프로젝트를 시작하기에 앞서 다른 팀원들이 이전에 진행한 프로젝트 코드를 리뷰했는데, Java에서 사용하는 카멜 케이스를 DB에 스네이크 케이스로 저장하기 위해 @Column 어노테이션을 써서 일일이 네이밍을 지정해준 것을 봤다.

네이밍을 바꾸는 게 아닌 스네이크 케이스로만 바꾸는 용도라면 어노테이션을 써서 이름을 지정해주지 않아도 스네이크 케이스로 바꿔준다고 해당 팀원에게 알려주었는데 알려주면서도 JPA가 바꿔주는건지, 하이버네이트가 바꿔주는 건지 헷갈려서 찾아보았다.

1. 하이버네이트(Hibernate)란?

하이버네이트(Hibernate)는 자바 언어를 위한 ORM(Oriented Relational Mapping) 프레임워크다. JPA의 구현체로, JPA 인터페이스를 구현하며 내부적으로 JDBC API를 사용한다.

JPA는 관계형 데이터베이스와 객체의 패러다임 불일치 문제를 해결할 수 있다는 점과 영속성 컨텍스트(엔터티를 영구 지정하는 환경) 제공이 큰 특징이다.

1-1. JPA

자바 애플리케이션에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스이다. 라이브러리가 아닌 인터페이스이므로 특정 기능을 하지는 않는다.

1-2. JDBC

자바 프로그래밍 언어와 다양한 데이터베이스 SQL 또는 테이블 형태의 데이터 사이에 독립적인 연결을 지원하는 표준이다. 즉, DB 작업을 위한 표준이다.

DBMS 회사들이 JDBC 인터페이스를 구현하여 제공한다. 이를 JDBC 드라이버라고 하는데, 결국 JDBC 드라이버란 DBMS 회사들이 자신들의 데이터베이스 시스템에 접근할 수 있도록 표준 JDBC 인터페이스에 명시된 메소드들을 구현한 것이다.

따라서 JDBC API를 사용할 경우 하나의 자바 응용 프로그램만으로 JDBC 드라이버를 제공하는 어떤 종류의 관계형 DBMS에도 접근이 가능하고, 사용자들은 특정 회사의 데이터베이스의 정확한 사용 방법을 몰라도 JDBC API만 알면 데이터베이스 조작이 가능하다.

2. 하이버네이트 명명 전략(Hibernate Naming Strategy)

하이버네이트5는 논리적 명명 전략(Logical Naming Strategy)물리적 명명 전략(Physical Naming Strategy)을 제공한다.

논리적 명명 전략을 통해 생성된 이름은 논리명이라고 하는데, 하이버네이트는 내부에서 이를 사용해 대상을 표시한다. 이는 데이터베이스에 있는 이름이 아니다. PhysicalNamingStrategy는 논리적 JPA 객체 이름을 기준으로 데이터 베이스에 사용되는 실제 물리적 이름을 제공한다. 실제 하이버네이트를 사용하면 데이터베이스 대상 이름을 직접 지정할 수 없고 논리 이름만 지정할 수 있다는 뜻이다.

2-1. 논리적 명명 전략(Logical Naming Strategy)

논리적 명명 전략은 암시적 명명 전략(Implicit Naming Strategy)명시적 명명 전략(Explicit Naming Strategy)으로 나눌 수 있다.

hibernate.implicit_naming_strategy 프로퍼티를 통해 암시적 명명 전략 구현 클래스를 설정할 수 있다.

1. 명시적 명명 전략(Explicit Naming Strategy)

@Table 또는 @Column 어노테이션을 통해 명시적으로 테이블 이름 또는 컬럼 이름을 지정해주는 방법이다.

@Table(name = "service_user")
@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "first_name")
    private String firstName;

    private String lastName;

    private String email;
}

엔터티가 위와 같이 구성되어 있을 때 엔터티 및 필드의 논리적 이름은 다음과 같다.

// 엔터티
User -> service_user

// 필드
firstName -> first_name
lastName -> lastName
email -> email

2. 암시적 명명 전략(Implicit Naming Strategy)

@Table 또는 @Column 어노테이션을 사용하지 않으면 하이버네이트는 암시적 명명 전략을 사용한다.

논리적 이름은 ImplicitNamingStrategy 인터페이스 구현체에 의해 논리적 이름이 결정되는데, 하이버네이트에서는 기본적으로 4개의 ImplicitNamingStrategy 인터페이스 구현체를 제공한다. (직접 구현해서 사용 가능하다.)

  • default
    • org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl
    • JPA가 default와 같다.
  • jpa
    • org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl
    • JPA 2.0 compliant naming strategy
  • legacy-jpa
    • org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImplcompliant with the legacy NamingStrategy developed for JPA 1.0
  • component-path
    • org.hibernate.boot.model.naming.ImplicitNamingStrategyComponentPathImpl

| User.java

@Entity
@Table
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name")
    private String username;

    private Integer age;
    private String emailAddress;

    @ManyToOne
    @JoinColumn
    private Team team;
}

| Team.java

@Entity
public class Team {

    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
}

| persistence.xml

<property name="hibernate.implicit_naming_strategy" value="org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl"/>
<property name="hibernate.implicit_naming_strategy" value="org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl"/>

ImplicitNamingStrategyJpaCompliantImpl 전략 사용시

create table Member (
    id bigint not null,
    age integer,
    emailAddress varchar(255),
    name varchar(255),
    team_id bigint,
    primary key (id)
)

create table Team (
    id bigint not null,
    name varchar(255),
    primary key (id)
)

ImplicitNamingStrategyLegacyHbmImpl 전략 사용시

create table Member (
    id bigint not null,
    age integer,
    emailAddress varchar(255),
    name varchar(255),
    team bigint,
    primary key (id)
)

create table Team (
    id bigint not null,
    name varchar(255),
    primary key (id)
)

2-2 물리적 명명 전략(Physical Naming Strategy)

PhysicalNamingStrategy 인터페이스를 구현하거나 PhysicalNamingStrategyStandardImpl 클래스를 상속받으면 물리적 이름을 구현할 수 있다.

hibernate.physical_naming_strategy 프로퍼티를 통해 물리적 명명 전략 구현 클래스를 설정할 수 있다.

ex. 카멜 케이스 → 스네이크 케이스

| SnakeCasePhysicalNamingStrategy.java

public class SnakeCasePhysicalNamingStrategy implements PhysicalNamingStrategy {

    @Override
    public Identifier toPhysicalCatalogName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        return convertToSnakeCase(name);
    }

    @Override
    public Identifier toPhysicalSchemaName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        return convertToSnakeCase(name);
    }

    @Override
    public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        return convertToSnakeCase(name);
    }

    @Override
    public Identifier toPhysicalSequenceName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        return convertToSnakeCase(name);
    }

    @Override
    public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        return convertToSnakeCase(name);
    }

    private Identifier convertToSnakeCase(Identifier identifier) {
        if (identifier == null) {
            return null;
        }
        String name = identifier.getText();
        String newName = name.replaceAll("([a-z])([A-Z])", "$1_$2").toLowerCase();
        return Identifier.toIdentifier(newName);
    }
}

| persistence.xml

<property name="hibernate.physical_naming_strategy" value="jpabook.start.SnakeCasePhysicalNamingStrategy"/>
create table member (
    id bigint not null,
    age integer,
    email_address varchar(255),
    name varchar(255),
    team bigint,
    primary key (id)
)

create table team (
    id bigint not null,
    name varchar(255),
    primary key (id)
)

3. Spring Boot와 하이버네이트

Spring Boot는 하이버네이트의 기본 인터페이스 구현을 덮어쓰고 SpringImplicitNamingStrategySpringPhysicalNamingStrategy(default)를 사용한다.

기본적으로 Spring Boot는 SpringPhysicalNamingStrategy을 사용하여 카멜 케이스(camelCase)나 파스칼 케이스(PascalCase)를 스네이크 케이스(snake_case)로 바꿔준다.

하이버네이트4

spring.jpa.hibernate.naming.physical-strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy

하이버네이트5

spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

4. Reference

[DB] 하이버네이트(Hibernate)란?

Hibernate 5 Naming Strategy Configuration - Baeldung

Hibernate 명명 정책: JPA 사양 및 Spring Boot 관점