Development/Code

Hibernate 6 로 마이그레이션: 새로운 기능과 변경된 사항

Danny Seo 2024. 8. 5. 11:43

목차

    Hibernate 6로 마이그레이션 하기

    안녕하세요 여러분 🫶

    이 글에서는 Hibernate 6.x로 마이그레이션할 때 필요한 주요 단계와 주의사항을 설명하려고 합니다. 또한 Hibernate 6의 새로운 기능도 살펴보겠습니다.

    Hibernate란 무엇인가요?

    Hibernate는 Java 기반의 객체-관계 매핑(ORM) 도구로, 객체 지향 도메인 모델을 관계형 데이터베이스에 매핑하는 기능을 제공합니다. 이는 데이터 지속성을 다루며, JDBC를 통해 관계형 데이터베이스와 상호 작용합니다.

     

    Hibernate 6의 새로운 점은 무엇인가요?

    Hibernate 6.0은 여러 내부 개선 사항들을 포함하고 있습니다. 이러한 변화들을 함께 살펴보겠습니다.

     

    Hibernate 6은 Jakarta Persistence API 3.x(JPA 3)를 사용합니다

    Hibernate 6.0.0.Final부터, Hibernate는 Jakarta Persistence API (JPA) 명세의 의존성을 버전 3으로 업데이트했습니다. 이는 기존의 javax.persistence.* 패키지 대신 jakarta.persistence.* 패키지를 사용해야 한다는 것을 의미합니다.

    따라서 모든 설정 파일과 import 문을 업데이트해야 하며, 이는 중요한 변경 사항입니다.

     

    Java 11로 업그레이드하는 것도 필요합니다. 아직 하지 않았다면 말이죠. Spring Boot 3에서는 최소 JDK 버전으로 Java 17을 요구하므로, Java 17로 전환할 것을 권장합니다.

     

    Spring Boot와 Spring Data JPA는 밀접하게 협력하며, Spring Boot 3는 기본적으로 Hibernate 6를 사용합니다. 따라서 Java 17로의 마이그레이션은 필수적입니다.

     

    기본 시퀀스 네이밍

    Hibernate 6로 마이그레이션한 후 가장 먼저 확인해야 할 것은 고유한 기본 키 값의 생성입니다. 만약 데이터베이스 시퀀스를 사용하고 각 엔티티에 지정된 시퀀스가 없다면, 새로운 Hibernate API에서 몇 가지 변경을 해야 합니다.

     

    여기서는 시퀀스를 정확히 정의하지 않고 GenerationType.SEQUENCE를 사용하여 생성 전략을 정의했습니다. 이름이 정의되지 않은 경우, Hibernate는 기본 시퀀스인 hibernate_sequence를 사용합니다.

    @Entity(name="Person")
    public class Person {
        @Id
        @GeneratedValue(strategy = GenerationType.SEQUENCE)
        private Long id;
        private String name;
    }

     

    Hibernate는 이 접근 방식을 변경했습니다. 이제 명시적으로 정의되지 않은 경우, Hibernate는 각 엔티티 클래스에 대해 다른 시퀀스를 사용합니다. 엔티티 이름과 접미사 _SEQ가 시퀀스 이름을 구성하며, 예를 들어 Person 엔티티의 경우 Person_SEQ가 됩니다.

     

    이것은 중요한 변화입니다. 왜냐하면 엔티티별 시퀀스가 데이터베이스에 존재하지 않기 때문입니다.

    이 문제를 해결하는 방법은 두 가지가 있습니다:

    1. 데이터베이스 스키마를 업데이트하여 새로운 시퀀스를 추가합니다.
    2. Hibernate가 이전 기본 시퀀스를 사용하도록 구성 매개변수를 추가합니다.

    이 두 가지 접근 방식 중에서 두 번째 접근 방식이 가장 빠르고 쉬운 방법이며, 이후 릴리스에서 새로운 시퀀스를 추가할 수 있습니다.

    persistence.xml 파일에서 hibernate.id.db_structure_naming_strategy 속성을 설정하면 Hibernate에게 이전 기본 시퀀스를 사용하도록 지시할 수 있습니다. 이 값을 single로 설정하면 Hibernate 5.3 미만의 기본 시퀀스를, legacy로 설정하면 Hibernate 5.3 이상의 기본 시퀀스 이름을 구성할 수 있습니다.
    <persistence>
        <persistence-unit name="my-persistence-unit">
            ...
            <properties>
                <!-- 하위 호환성 보장 -->
                <property name="hibernate.id.db_structure_naming_strategy" value="legacy" />
                ...
            </properties>
        </persistence-unit>
    </persistence>
    Spring Boot와 함께 Hibernate 6 마이그레이션을 수행하는 경우, 다음과 같이 속성을 설정할 수 있습니다:
    # Hibernate >=5.3
    spring.jpa.properties.hibernate.id.db_structure_naming_strategy: legacy
    
    # Hibernate <5.3
    spring.jpa.properties.hibernate.id.db_structure_naming_strategy: single

     

    Instant 및 Duration 매핑의 변경

    Hibernate 6으로 마이그레이션할 때 또 다른 중요한 변경 사항은 Instant와 Duration의 매핑입니다.

     

    Hibernate 5에서는 InstantSqlType.TIMESTAMP에 매핑되고 DurationTypes.BIGINT에 매핑되었습니다. 하지만 Hibernate 6에서는 InstantSqlType.TIMESTAMP_UTC에 매핑되고 DurationSqlType.INTERVAL_SECOND에 매핑됩니다.

     

    이 변경으로 인해 기존 애플리케이션의 테이블 매핑이 깨질 수 있습니다. 이러한 문제가 발생하면 hibernate.type.preferred_duration_jdbc_type 속성을 BIGINT로, hibernate.type.preferred_instant_jdbc_type 속성을 TIMESTAMP로 설정하여 문제를 해결할 수 있습니다.

    <persistence>
        <persistence-unit name="my-persistence-unit">
            <properties>
                <!-- 하위 호환성 보장 -->
                <property name="hibernate.type.preferred_duration_jdbc_type" value="BIGINT" />
                <property name="hibernate.type.preferred_instant_jdbc_type" value="TIMESTAMP" />
                ...
            </properties>
        </persistence-unit>
    </persistence>

    Hibernate 6은 이러한 두 가지 추가 구성 매개변수를 도입했지만, 둘 다 Incubating으로 표시되어 향후 변경되거나 제거될 수 있습니다.

     

    UUID 생성 방식 변경

    Hibernate 버전 5에서는 UUID 생성기가 암묵적으로 설정되었습니다. Hibernate는 ID 필드 유형을 감지한 후 UUID 랜덤 생성기를 자동으로 감지했습니다. 그러나 Hibernate 6부터는 UUID 기본 키를 다음과 같이 정의해야 합니다:

    @Id
    @UuidGenerator(style = UuidGenerator.Style.AUTO)
    @GeneratedValue
    private UUID id;

    이 방식으로 생성기와 UUID 생성 스타일을 명시적으로 정의할 수 있습니다. UuidGenerator.Style에는 다음과 같은 유형이 있습니다:

    • AUTO: 기본적으로 RANDOM 사용
    • RANDOM: UUID.randomUUID()를 사용하여 값 생성
    • TIME: IETF RFC 4122와 일치하는 시간 기반 생성 전략 적용

     

    JSON 문서 매핑의 간소화

    Hibernate 6은 JSON 문서를 Java 객체로 변환하고 이를 데이터베이스에 저장하는 과정을 훨씬 더 간편하게 해줍니다. Hibernate 4와 5 버전에서는 JSON 문서를 데이터베이스에 읽고 쓰는 방법과 이를 직렬화 및 역직렬화하는 방법을 Hibernate에 지시하기 위해 사용자 정의 UserType을 정의해야 했습니다.

     

    Hibernate 6 버전부터는 JSON 문서를 Embeddable 객체로 매핑할 수 있습니다. Embeddable 객체는 고유한 식별자가 없는 Java 클래스입니다. 이는 테이블의 여러 열에 상태를 매핑하지만 @Id 속성은 가지지 않습니다. 이렇게 하면 엔티티 정의의 일부가 되어 동일한 데이터베이스 테이블에 매핑됩니다.

     

    Java 클래스를 구현하고 @Embeddable 주석을 달아 Embeddable 객체를 정의할 수 있습니다.

    @Embeddable
    public class Address implements Serializable {
        private String street;
        private String postalCode;
    }

    이제 Embeddable 객체를 엔티티 속성으로 사용할 수 있습니다. 이를 위해 속성에 추가로 @Embedded 주석을 추가해야 합니다. 이 주석은 Hibernate에게 Embeddable 클래스에서 추가 매핑 데이터를 가져오도록 지시합니다.

     

    Embeddable 객체를 JSON 문서로 매핑하려면 @JdbcTypeCode(SqlTypes.JSON) 주석을 추가하고 클래스 경로에 JSON 매핑 라이브러리를 포함해야 합니다.

    @Entity(name="Person")
    public class Person {
        @Id
        @GeneratedValue(strategy = GenerationType.SEQUENCE)
        private Long id;
        private String name;
    
        @Embedded 
        @JdbcTypeCode(SqlTypes.JSON)
        private Address address;
    }

    이렇게 하면 Hibernate는 속성을 JSON 문서로 직렬화 및 역직렬화하여 데이터베이스에 저장합니다. 데이터베이스 방언에 따라 Hibernate가 속성을 매핑하는 열의 유형이 결정됩니다.

     

     

    Embeddable로서 레코드 지원

    Hibernate는 Hibernate 6 이전까지 DTO 투영으로서의 레코드만 지원했습니다. 하지만 Hibernate 6 출시와 함께 Embeddable로서 레코드를 매핑할 수 있습니다. 불행히도 엔터티를 모델링하는 데에는 레코드를 사용할 수 없습니다.

     

    버전 6.0 및 6.1에서 Embeddable로 사용하려는 각 레코드에 대해 EmbeddableInstantiator를 구현해야 합니다.

     

    EmbeddableInstantiator는 Embeddable 객체를 인스턴스화하는 방법을 Hibernate에 알려주는 독점 Hibernate 인터페이스입니다. 이를 통해 레코드를 Embeddable로 모델링할 수 있으며, JPA의 기본 생성자 요구 사항을 없앨 수 있습니다.

     

    Hibernate는 버전 6.2부터 모든 Embeddable 레코드에 대해 자체 EmbeddableInstantiator를 제공합니다.

     

    레코드를 Embeddable로 정의하려면 레코드를 Embeddable 주석으로 주석 처리하면 됩니다. Hibernate 버전이 6.0 또는 6.1인 경우 EmbeddableInstantiator 주석을 추가하고 인스턴스화 구현을 참조해야 합니다.

    @Embeddable
    @EmbeddableInstantiator(PersonInitiator.class)
    public record Person (String id, String name) {}
    
    public class PersonInitiator implements EmbeddableInstantiator {
        public Object instantiate(ValueAccess valuesAccess, SessionFactoryImplementor sessionFactory) {
            final String id = valuesAccess.getValue(0, String.class);
            final String name = valuesAccess.getValue(1, String.class);
            return new Person(id, name);
        }
    }

     

    이 글에서는 Hibernate 6.x로 마이그레이션할 때 필요한 주요 단계와 주의사항을 설명하였습니다.

     

    읽어주셔서 감사합니다! 😊
    개발 관련 궁금증이나 고민이 있으신가요?
    아래 링크를 통해 저에게 바로 문의해 주세요! 쉽고 빠르게 도움 드리겠습니다.

    '개발자서동우' 프로필 보기

     

     

    당신에게 꼭 도움이 될 수 있는 기술 사이트를 소개합니다 :

    사이트 이동하기

    devloo.io :: 스프링 카테고리