[JPA] 엔티티 생성시 Enumerated 와 hibernate 타입 검증 에러 해결 하기

728x90

안녕하세요👋

오늘은 제가 개발시 느꼈던 에러와 불편함에 대한 해결책을 이야기 해보려고 합니다.

 

해결 방법이 급하게 궁금하신분은 맨 아래에 해결 방법이 나와 있습니다ㅎㅎ

 

서론

제 개발 환경은 Intellj, Java17, SpringBoot3.2, MySQL8.0 입니다 (참고 해주세요)

 

저는 항상 DB 툴을 통해서 미리 ERD 를 통해 DB 설계 후 , DB에 알맞게 Entity 를 구현하였습니다.

 

그리고 실무라고 생각하고 개발을 진행하였기에 application.yml 에서 JPA 설정은 

spring:
  jpa:
    show-sql: true
    properties:
        format_sql: true
        dialect: org.hibernate.dialect.MySQL8Dialect
    hibernate:
      ddl-auto: validate

 

ddl-autovalidate 로 잡아두고 엔티티 구현을 했습니다.

 

간단하게 ddl-auto Rule 에 대한 설명을 하자면 

#개발 초기 단계 또는 로컬에서 테스트 : create 또는 update
#테스트 서버 : update 또는 validate
#스테이징 및 운영 서버 : validate 또는 none

 

실무에서는 위 운영 방식을 가지고 설계를  합니다.

 

위 처럼 설정을 하고 엔티티 설계를 하였고,  DB 설계 시 불변의 값을 가지는 Status 값을 저는 varchar(50) 으로 즉 String 타입으로

설정해 두었습니다. 아래는 제가 엔티티를 설계한 코드 입니다. 

	@Column(length = 50, nullable = false)
	@Enumerated(EnumType.STRING) // 열거형 -> 문자열로 저장
	private UserOrderStatus status;

 

위 처럼 설계를 한 후에 서버를 실행 해보니

 

🤪 위 오류가 발생하였습니다.

 

오류 내용을 자세히 살펴보니, Hiberante 에서 ddl-auto: validation 에 의해 컬럼들을 검증하는 과정에서

column type 이 일치하지 않는 것이 있다는 이야기 였습니다.

 

자세히 살펴보니 Entity 설계 할 때 status 컬럼이랑 db 에 있는 status 컬럼이랑 타입이 불일치 한다는 이야기 였습니다.

 

이제 본격적으로 해결해보는 이야기를 해보겠습니다.

 

✅ 본론

왜 오류가 났을까? 라는 생각이 먼저 들었고 어디선가 주어 들은게 있어서 yml 파일을 찾았고, yml 파일에서 단서를 찾았다.

 

ddl-auto: validate 를 -> ddl-auto: update 로 바꾸니 오류가 생기지 않았다

 

그래서 문서들을 찾아보니 ddl-auto: update 는 말 그대로 update 를 시키긴 하는데,

기존에 존재하는 컬럼의 속성(nullable, 크기, 데이터 타입 등) 은 건드리지 않고 새로운 컬럼이 추가되는 변경사항만 반영한다고 합니다. 

 

간단하게 말해 db 에 있는 name 타입은 int 이고, entity 설계시 name 은 String 이여도 그냥 오류 없이 진행이 된다는 것 입니다. 

update 는 컬럼의 속성은 건드리지 않는다는 뜻 입니다.

 

이래서 오류가 나지 않았던 것입니다.

 

그렇다면 Validate 는 ??

 

validate 는 다른 속성들과 다르게 DDL 을 작성하면서 생성,변경을 해주는 것이 아닌, Entity 와 DB 가 정상적으로 매핑이 되는지만 검사합니다. 컬럼 이름과, 컬럼 속성이 다르면 예외를 발생시키면서 어플리케이션을 종료합니다.

 

즉 저는, DB 와 Entity 에 Status 속성이 달랐기 때문에 나는 오류라는걸 확신했습니다. 

 

그럼 이제 저는 원인을 찾았으니 해결방법을 고민해봤습니다.

 

❓ 어떻게 하면 Entity 와 DB 속성을 맞출 수 있을까 ❓

 

방법은 간단하죠

1) Entity 에서 어떠한 작업을 통해, DB 와 속성을 일치시켜주기

2) DB 에서 Entity 와 맞는 속성으로 테이블을 생성하기

 

다행이도 제가 고민한 내용이 둘다 해결책이 있어 다행이였습니다.

 

1번 방법

Entity 에서 이 컬럼은 enum 이라고 명시를 해주는 것입니다. 

 

어떻게 명시를 하냐면 바로 @Column 어노테이션 안에서 답을 찾을 수 있었습니다.

 

위 코드는 @Column 어노테이션 내부 로직 입니다.

 

이 부분에서 columnDefinition 에 주목해야 합니다. 

(Optional) The SQL fragment that is used when generating the DDL for the column. Defaults to the generated SQL to create a column of the inferred type.

 

columnDefinition 에 공식문서 설명 입니다.

 

번역기를 돌려보니 

(선택 사항) 열에 대한 DDL을 생성할 때 사용되는 SQL 조각입니다. 유추된 유형의 열을 생성하려면 생성된 SQL을 기본값으로 사용합니다.

 

즉 DDL 생성시 @Column 어노테이션의 columnDefinition 속성은 엔티티의 특정 필드에 대한 데이터베이스 열의 정의를 직접 지정하는 데 사용됩니다. 이 속성을 사용하면 JPA가 기본 데이터베이스 열 정의를 무시하고 사용자가 제공한 정의를 사용하게 됩니다.

 

그리고 위 해결 방법을 통해 제 코드에 적용하였습니다. 

	@Column(columnDefinition = "varchar(50)", nullable = false)
	@Enumerated(EnumType.STRING)
	private UserOrderStatus status;

 

 

2번 방법

mysql 테이블 설계시 status 를 enum 으로 준다.

CREATE TABLE table_name (
    id INT AUTO_INCREMENT,
    status ENUM('ORDER', 'UNORDER') NOT NULL,
    PRIMARY KEY (id)
);

 

아주아주 간다합니다. 

치명적인 단점으로는 enum은 모든 RDB에서 제공하는 일반적인 기능이 아니기에 다른 rdb로 마이그레이션 할 경우 문제가 생길 수 있다.

그리고 데이터 수정이 어렵다는 단점이 있습니다. 

 

위처럼 DB 설계 후 entity 생성시

@Entity(name="enumtest")
public class enumtest {

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

	@Enumerated(EnumType.STRING)
	private Status status;
}

@Getter
public enum Status {
	ORDER("등록"),
	UNORDER("해지")
	;

	private String description;
}

 

위 처럼 설계를 하면은 오류가 발생하지 않습니다.

 

MySQL 에 enum 타입이 있는데 왜 사용을 지양한다는 이야기들이 많습니다.

그 이야기는 추후에 다시 다뤄서 포스팅을 해보겠습니다. 

 

결론 

1) Entity 에서 명시를 해라
@Column(columnDefinition = "varchar(50)")
@Enumerated(EnumType.STRING)
private UserOrderStatus status;


2) DB 생성시 타입을 enum 으로 해라
CREATE TABLE table_name (
    id INT AUTO_INCREMENT,
    status ENUM('ORDER', 'UNORDER') NOT NULL,
    PRIMARY KEY (id)
);

 

추가적인 이야기로 예전에는 명시가 없이도 되었는데, Hibernate6.2 부터는 매핑 검증방식이 조금 더 엄격해졌고

MySQL DB 인 경우 Java Enum 매핑을 MySQL enum 타입을 사용하도록 전략이 변경되었기 때문도 있습니다.

 

위 방법을 통해 DB 와 Hibernate 엔티티 사이의 일관성을 유지하고, 예상치 못한 타입 불일치로 인한 오류를 방치하는데 도움을 줄 수 있습니다.

 

이상 포스팅 마치겠습니다. 감사합니다.

 

 

 

ref: https://velog.io/@superkkj/Hibernate-6-Enum%EA%B4%80%EB%A0%A8

728x90