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;
참고한 사이트