MyBatis-Plus의 열거형 처리 개요
MyBatis를 사용할 때 가장 번거로운 작업 중 하나는 Java의 열거형(Enum)과 데이터베이스의 컬럼 값을 매핑하는 것입니다. 기본적으로 MyBatis는 열거형의 이름(String)이나 순서(Ordinal)를 저장하지만, 실제 비즈니스 환경에서는 특정 코드값(예: 1, 10, 20)을 저장해야 하는 경우가 많습니다. MyBatis-Plus는 MybatisEnumTypeHandler를 통해 별도의 TypeHandler 작성 없이도 이를 우아하게 해결할 수 있는 메커니즘을 제공합니다.
열거형 매핑을 위한 두 가지 핵심 전략
1. IEnum 인터페이스 구현 방식 (권장)
MyBatis-Plus 3.4.0 버전부터 권장되는 방식으로, 열거형이 IEnum<T> 인터페이스를 상속받아 값을 반환하는 getValue() 메서드를 구현하는 형태입니다.
public enum UserStatus implements IEnum<Integer> {
NORMAL(1, "정상"),
BANNED(0, "정지"),
DELETED(-1, "삭제");
private final int code;
private final String description;
UserStatus(int code, String description) {
this.code = code;
this.description = description;
}
@Override
public Integer getValue() {
return this.code;
}
}
이 방식은 제네릭을 통해 저장될 데이터 타입을 명확히 정의할 수 있어 타입 안정성이 높고 코드가 정돈된다는 장점이 있습니다.
2. @EnumValue 어노테이션 활용
인터페이스 구현이 어렵거나 기존에 작성된 열거형 코드를 유지해야 할 때 유용합니다. 데이터베이스에 저장될 필드에 @EnumValue 어노테이션을 선언하면 됩니다.
public enum RoleType {
ADMIN(100, "관리자"),
MANAGER(50, "매니저"),
USER(10, "일반사용자");
@EnumValue
private final int roleCode;
private final String roleName;
RoleType(int roleCode, String roleName) {
this.roleCode = roleCode;
this.roleName = roleName;
}
public int getRoleCode() { return roleCode; }
}
엔티티 적용 및 데이터베이스 설계
정의된 열거형은 엔티티 클래스에서 일반 필드처럼 선언하여 사용합니다. MyBatis-Plus는 내부적으로 해당 필드의 타입을 감지하여 적절한 처리기를 할당합니다.
@TableName("app_user")
public class UserEntity {
private Long id;
private String username;
// IEnum 구현체 사용
private UserStatus status;
// @EnumValue 사용
private RoleType role;
}
데이터베이스 설계 시에는 해당 열거형의 value 또는 code 타입에 맞춰 컬럼 타입을 정의해야 합니다. 예를 들어 위 예시의 경우 status와 role 컬럼은 모두 INT 타입으로 설계하는 것이 적절합니다.
전역 설정 및 패키지 스캔
Spring Boot 환경에서는 application.yml 설정을 통해 열거형이 위치한 패키지를 지정하고 기본 TypeHandler를 등록해야 합니다.
mybatis-plus:
# 열거형 클래스들이 위치한 패키지 경로
type-enums-package: com.example.project.enums
configuration:
# 기본 열거형 핸들러 설정
default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
조건부 쿼리에서의 열거형 활용
MyBatis-Plus의 Wrapper를 사용하여 쿼리를 작성할 때, 별도의 값 변환 없이 열거형 객체를 직접 전달할 수 있습니다.
// LambdaQueryWrapper 활용 예시
List<UserEntity> activeAdmins = userMapper.selectList(
Wrappers.<UserEntity>lambdaQuery()
.eq(UserEntity::getStatus, UserStatus.NORMAL)
.eq(UserEntity::getRole, RoleType.ADMIN)
);
내부적으로 MybatisEnumTypeHandler가 UserStatus.NORMAL 객체에서 getValue()를 호출하거나 @EnumValue가 붙은 필드 값을 추출하여 SQL 파라미터로 바인딩합니다.
성능 최적화 및 동작 원리
MyBatis-Plus는 매번 리플렉션을 통해 열거형 정보를 분석하지 않습니다. TABLE_METHOD_OF_ENUM_TYPES와 같은 내부 ConcurrentHashMap 구조를 사용하여 열거형 클래스의 메타데이터를 캐싱합니다.
- 초기 구동 시 또는 첫 호출 시 열거형의 구조를 분석하여 캐시에 저장합니다.
- 이후 모든 CRUD 작업은 캐싱된 정보를 바탕으로 실행되므로 런타임 성능 저하가 거의 없습니다.
- 대량의 데이터를 처리하는 배치 작업에서도 숫자 또는 문자열 변환 수준의 가벼운 연산만 수행됩니다.
실무 적용 시 고려사항
- 일관성 유지: 한 프로젝트 내에서는
IEnum방식과@EnumValue방식 중 하나를 선택하여 통일감 있게 사용하는 것이 유지보수에 유리합니다. - 타입 일치: 데이터베이스의 컬럼 타입과 Java 열거형에서 반환하는 값의 타입이 일치하는지 반드시 확인해야 합니다.
- Unknown Value 처리: 데이터베이스에 정의되지 않은 값이 들어있을 경우를 대비하여 예외 처리 전략이나 기본값 설정을 검토해야 합니다.