포스트

Hibernate - MySQL에 Enum 필드를 String(varchar)로 저장하는 이슈

환경

  • Spring Boot 3.2.3
  • Hibernate 6.4.4
  • MySQL 8
  • Java 17


학습 도중에 다음과 같은 에러를 만났다.

Caused by: org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: wrong column type encountered in column [status] in table [user]; found [varchar (Types#VARCHAR)], but expecting [enum (‘registered’,’unregistered’) (Types#ENUM)]


발생 원인

java에서 enum으로 관리되는 데이터를 MySQL에 varchar 타입으로 저장되도록 하고 싶어서 아래와 같이 코드를 작성한 후 실행했더니 해당 에러를 만나게 되었다.

application.yml 설정 일부

1
2
3
4
spring:  
  JPA:  
    hibernate:  
      ddl-auto: validate

ddl 일부

1
status          VARCHAR(50)  NOT NULL,

JPA entity 일부

1
2
3
@Column(length = 50, nullable = false)     
@Enumerated(EnumType.STRING) 
private UserStatus status;


해결

JPA Entity에서 문제의 필드에 @Column 어노테이션의 columnDefinition 속성 값으로 varchar를 지정해주어 해결!

1
2
3
@Column(columnDefinition = "varchar(50)", nullable = false)
@Enumerated(EnumType.STRING) 
private UserStatus status;




분석

에러의 발생 이유는 JPA에 정의한 엔티티의 Enum 필드가 DB 테이블에 들어가야 할 컬럼의 타입과 맞지 않았기 때문이었다.

db 엔진으로 MySQL을 사용했기 때문인데, MySQL은 Enum 타입을 다루기 때문에 hibernate가 해당 필드의 데이터 타입을 Enum으로 정의하여 db table을 검색해서 에러가 나타났다.

# 문제가 발생한 JPA 엔티티

1
2
3
4
5
6
7
8
@Entity(name = "user")
public class UserEntity extends BaseEntity {  
	..
	@Column(length = 50, nullable = false)
	@Enumerated(EnumType.STRING)
	private UserStatus status;  
	..
}

# 먼저 생성했던 MySQL DDL 일부

1
2
3
4
5
6
CREATE TABLE user
(
	..
	status          VARCHAR(50)  NOT NULL,
	..
);



MySQL은 Enum 타입을 다루기 때문에 JPA Entity에서 Enum 타입의 필드를 정의할 때 @Enumerated(EnumType.STRING) 어노테이션만 붙일 경우 해당 필드는 varchar가 아닌 Enum 타입으로 MySQL에 저장된다.

# 문제의 JPA 엔티티를 ddl-auto create를 이용하여 생성한 DDL 일부

1
2
3
4
5
6
[Hibernate] 
    create table user (
	    ...
        status enum ('REGISTERED','UNREGISTERED') not null,
	    ...
    ) engine=InnoDB


그렇기 때문에 MySQL에 varchar 타입으로 Java의 Enum 데이터를 저장하고 싶다면 @Column(columnDefinition = "varchar()") 어노테이션을 추가하여 컬럼의 데이터 타입을 설정해주어야 한다.



다른 궁금증

만약 @Enumerated(EnumType.STRING) 어노테이션을 삭제하고 생성한다면 MySQL에 어떤 형태로 저장될까 궁금해져서 @Enumerated(EnumType.STRING) 어노테이션을 삭제하고 실행해 보았다.

# JPA Entity의 Enum 타입 필드에 @Enumerated 삭제

1
2
@Column(length = 50, nullable = false)
private UserStatus status;

# JPA ddl-auto create를 이용해 생성된 DDL

1
2
3
4
5
6
[Hibernate] 
    create table user (
	    ...
        status tinyint not null check (status between 0 and 1),
	    ...
    ) engine=InnoDB


1바이트 정수 타입인 tinyint 자료형으로 저장되는 것을 확인했다.

MySQL의 tinyint는 length 속성이 적용되지 않기 때문에 JPA에서 @Column(length) 어노테이션으로 정의한 length 속성 값은 무시되었다.

MySQL을 통해 enum 데이터를 ordinal로 관리하고자 한다면 아래와 같이 사용하면 되겠다!

1
2
@Column(nullable = false)
private UserStatus status;




참고한 사이트

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.