• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    해결됨

Entity와 MySQL 매핑 관련 질문 드립니다.

24.02.16 03:01 작성 24.02.16 03:12 수정 조회수 212

0

안녕하세요.

Spring Boot 3.2.2 + MySQL 8.0 환경에서 JPA 학습 중 질문이 있습니다.

 

  1. Entity가 Enum 타입인 경우, MySQL Column 매핑은 어떤식으로 되어야 Best Practice 일까요?


    학습 과정 중에서는 `@Column(columnDefinition = "char(1)")` Annotation으로 매핑 문제를 해결했습니다. 실무에서는 어떤 방식으로 정의하는지 알고 싶습니다.
    (e.g. Column 타입을 varchar(255)로 정의해서 사용한다. 아니면 DB 타입에 맞춰 Entity 정의하여 사용한다.)

    1. Entity에서 사용된 Enum의 경우 'Y', 'N' 값만 정의되어 있습니다.

      public enum BasicStatus {
          Y, N
      }
    2. MySQL Field 타입은 char(1)로 정의되어 있습니다.

       

      @Entity
      @Getter
      @NoArgsConstructor(access = AccessLevel.PROTECTED)
      public class Destination extends BaseDatetimeEntity {
      
        // ... 중략
      
        @Enumerated(EnumType.STRING)
        @Column(columnDefinition = "char(1)")
        private BasicStatus apartment;
      }

     

  2. Table을 직접 DDL로 생성 후, ddl-auto 옵션을 validate로 설정하여 서버를 실행하면 "Schema-validation: missing table [destination_seq]" 에러가 발생합니다. 해당 문제를 해결하기 위해 어떻게 접근하면 될까요? 그리고 실무에서는 Table 생성 시, JPA를 통한 생성을 하는 지 DDL로 생성하는지 궁금합니다. (DB 학습이 부족한 상황에서 질문 드리는 점 양해 부탁드립니다.)

     

    1. Entity

       

      @Entity
      @Getter
      @NoArgsConstructor(access = AccessLevel.PROTECTED)
      public class Member {
      
          @Id @GeneratedValue
          @Column(name = "member_id")
          private Long id;
          
          // ... 중략
      
          @OneToMany(mappedBy = "member")
          private List<Destination> destinations = new ArrayList<>();
      
      } 
      @Entity
      @Getter
      @NoArgsConstructor(access = AccessLevel.PROTECTED)
      public class Destination extends BaseDatetimeEntity {
      
          @Id @GeneratedValue
          @Column(name = "destination_id")
          private Long id;
      
          @ManyToOne(fetch = FetchType.LAZY)
          @JoinColumn(name = "member_id")
          private Member member;
      
          // ... 중략
      }
    2. DDL

      create table member (
          member_id bigint unsigned auto_increment comment '회원 ID',
          # ... 중략
          primary key (member_id)
      );
      
      create table destination (
          destination_id bigint unsigned auto_increment comment '배송지 ID',
          member_id bigint unsigned not null comment '회원 ID',
          # ... 중략
          primary key (destination_id),
          foreign key (member_id) references member(member_id)
      );
    3. Error Log

       

      org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing table [destination_seq]
      	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1773) ~[spring-beans-6.1.3.jar:6.1.3]
      	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:599) ~[spring-beans-6.1.3.jar:6.1.3]
      	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:521) ~[spring-beans-6.1.3.jar:6.1.3]
      	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) ~[spring-beans-6.1.3.jar:6.1.3]
      	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.3.jar:6.1.3]
      	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) ~[spring-beans-6.1.3.jar:6.1.3]
      	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-6.1.3.jar:6.1.3]
      	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1231) ~[spring-context-6.1.3.jar:6.1.3]
      	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:949) ~[spring-context-6.1.3.jar:6.1.3]
      	at org.springframework.context.support.AbstractApplicationContext.__refresh(AbstractApplicationContext.java:624) ~[spring-context-6.1.3.jar:6.1.3]
      	at org.springframework.context.support.AbstractApplicationContext.jrLockAndRefresh(AbstractApplicationContext.java:41002) ~[spring-context-6.1.3.jar:6.1.3]
      	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:42008) ~[spring-context-6.1.3.jar:6.1.3]
      	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.2.2.jar:3.2.2]
      	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-3.2.2.jar:3.2.2]
      	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[spring-boot-3.2.2.jar:3.2.2]
      	at org.springframework.boot.SpringApplication.run(SpringApplication.java:334) ~[spring-boot-3.2.2.jar:3.2.2]
      	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1354) ~[spring-boot-3.2.2.jar:3.2.2]
      	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1343) ~[spring-boot-3.2.2.jar:3.2.2]
      	at kr.co.asasac.Application.main(Application.java:10) ~[main/:na]
      Caused by: jakarta.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing table [destination_seq]
      	at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:421) ~[spring-orm-6.1.3.jar:6.1.3]
      	at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396) ~[spring-orm-6.1.3.jar:6.1.3]
      	at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:352) ~[spring-orm-6.1.3.jar:6.1.3]
      	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1820) ~[spring-beans-6.1.3.jar:6.1.3]
      	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1769) ~[spring-beans-6.1.3.jar:6.1.3]
      	... 18 common frames omitted
      Caused by: org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing table [destination_seq]
      	at org.hibernate.tool.schema.internal.AbstractSchemaValidator.validateTable(AbstractSchemaValidator.java:134) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final]
      	at org.hibernate.tool.schema.internal.GroupedSchemaValidatorImpl.validateTables(GroupedSchemaValidatorImpl.java:46) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final]
      	at org.hibernate.tool.schema.internal.AbstractSchemaValidator.performValidation(AbstractSchemaValidator.java:97) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final]
      	at org.hibernate.tool.schema.internal.AbstractSchemaValidator.doValidation(AbstractSchemaValidator.java:75) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final]
      	at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:295) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final]
      	at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.lambda$process$5(SchemaManagementToolCoordinator.java:145) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final]
      	at java.base/java.util.HashMap.forEach(HashMap.java:1429) ~[na:na]
      	at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:142) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final]
      	at org.hibernate.boot.internal.SessionFactoryObserverForSchemaExport.sessionFactoryCreated(SessionFactoryObserverForSchemaExport.java:37) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final]
      	at org.hibernate.internal.SessionFactoryObserverChain.sessionFactoryCreated(SessionFactoryObserverChain.java:35) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final]
      	at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:315) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final]
      	at org.hibernate.boot.internal.SessionFactoryBuilderImpl.__build(SessionFactoryBuilderImpl.java:450) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final]
      	at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:41010) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final]
      	at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.__build(EntityManagerFactoryBuilderImpl.java:1507) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final]
      	at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:40002) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final]
      	at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.__createEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:75) ~[spring-orm-6.1.3.jar:6.1.3]
      	at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:40003) ~[spring-orm-6.1.3.jar:6.1.3]
      	at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.__createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:376) ~[spring-orm-6.1.3.jar:6.1.3]
      	at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:45002) ~[spring-orm-6.1.3.jar:6.1.3]
      	at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) ~[spring-orm-6.1.3.jar:6.1.3]
      	... 22 common frames omitted

감사합니다.

답변 3

·

답변을 작성해보세요.

1

y2gcoder님의 프로필

y2gcoder

2024.02.16

안녕하세요. Anonymous님, 공식 서포터즈 y2gcoder입니다.

  1. 결국 선택에 따라 다를 것 같습니다. DDL을 따로 만들 때 char(1) 로 설정해놨다면 저도 Anonymous 님과 같이 사용할 것 같습니다. 해당 enum의 모든 문자열 길이가 1로 고정되어있다면 해당 컬럼 타입이 맞을 것 같습니다! 다만 enum의 타입 길이가 각각 다르다면 varchar 타입을 고려해볼 것 같습니다 :)

  2. 이건 바뀐 AUTO 정책과 관련이 있는 것 같습니다! 하이버네이트 버전이 올라감에 따라 AUTO 일 때의 기본 전략은 GenerationType.Table 로 변경되었습니다! 다음 링크(클릭)에서 확인해주시면 감사하겠습니다! 이거랑 추가로 스프링 부트 3.2.2 버전에서 사용하는 Hibernate 6.3 버전 문서(클릭)에서도 AUTO 전략에 대한 설명이 있습니다!
    imageAUTO를 선택하면 DDL에 작성했던 autoincrement를 사용하지 않고 SEQUENCE, TABLE, UUID 중 하나를 사용한다고 합니다 :)

 

감사합니다.

Anonymous님의 프로필

Anonymous

질문자

2024.02.16

감사합니다.

0

Anonymous님의 프로필

Anonymous

질문자

2024.02.16

  1. "Schema-validation: missing table [destination_seq]" 에러는 strategy = GenerationType.IDENTITY를 명시적으로 정의하여 해결되었습니다. 문제가 왜 발생하게 되었는지는 잘 모르겠습니다. 답변 부탁드리겠습니다~

    1. 참고로 @GeneratedValue의 기본값은 GenerationType.AUTO 로 알고 있습니다. MySQL에서는 strategy = GenerationType.IDENTITY 사용되는 걸로 알고 있습니다. identity 전략은 데이터베이스에 엔티티를 저장한 후 식별자 값을 가져와 영속성 컨텍스트에 저장하여 사용하는 걸로 알고 있습니다.

0

인프런 AI 인턴님의 프로필

인프런 AI 인턴

2024.02.16

안녕하세요, 인프런 AI 인턴입니다. 질문자님의 경우 관련된 구체적인 케이스에 대한 답변을 찾기 위해, Entity와 Enum 타입, 그리고 DDL과 관련된 문제 해결 방법을 찾았습니다.

  1. Entity가 Enum 타입인 경우의 MySQL Column 매핑에 대한 Best Practice

    여기서 질문자님이 제시한 매핑 문제 해결 방법은 하나의 방안으로 볼 수 있으며, 실제로 다양한 방법이 있을 수 있습니다. 참고하면 좋을 유사한 질문으로, “MySQL enum 대소문자” 에 관한 내용이 있으며, 여기에서는 MySQL의 Enum 타입과 Java의 Enum 타입을 매핑하는 방법에 대한 토론이 있습니다. 하이버네이트가 데이터베이스와 클래스 필드를 매핑할 때 요구하는 타입이 상이하면 문제가 발생할 수 있는데, 이 경우에는 @Enumerated(EnumType.STRING) 애너테이션을 사용하면서 String 타입으로 변환을 하여 문제를 해결할 수 있습니다.

    마찬가지로 ”@Enumerated와 hibernate 타입 검증 관련” 질문에서는 Hibernate가 기대하는 Enum 타입과 실제 데이터베이스의 VARCHAR 타입 사이의 충돌 문제에 대한 논의가 있습니다. @Column 애너테이션과 @Enumerated(EnumType.STRING) 애너테이션 사용을 통해 해결하는 예시를 찾을 수 있어 이는 질문자님의 상황에도 도움이 될 수 있습니다.

  2. DDL을 통한 Table 생성과 JPA 설정 문제

    Schema-validation 에러에 대한 질문은, @GeneratedValue 애너테이션 사용 방법 누락 또는 잘못된 사용으로 인한 것일 수 있습니다. @GeneratedValue 애너테이션은 기본적으로 기본키 생성을 위한 전략을 명시하는 데 사용됩니다. 만약 @GeneratedValue(strategy = GenerationType.SEQUENCE)와 같이 설정되어 있는 경우, Hibernate는 이에 해당하는 Sequence를 찾으려고 합니다. MySQL에서는 Sequence 대신 Auto Increment를 사용하는 것이 더 일반적이므로, @GeneratedValue(strategy = GenerationType.IDENTITY)를 사용하여 문제를 해결할 수 있습니다. 이와 관련해서 “챕터6의 다양한 연관관계 매핑 - ManyToMany 전환작업시 질문드립니다.” 질문을 참고하시면 어느 정도 해결책을 얻을 수 있을 것 같습니다.

실무에서의 Best Practice는 환경과 팀의 정책에 따라 다양할 수 있으며, 일부 조직에서는 Entity 정의 시 DDL로 데이터베이스 스키마를 생성하고 ddl-auto 옵션을 update로 설정하거나, 매뉴얼로 테이블을 생성 후 validate로 설정하기도 합니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.