서론 (Introduction)

JPA를 사용한 스프링 프로젝트를 기존에 존재하던 오라클 데이터베이스에 접속하자 JPA - Value '0000-00-00' can not be represented as java.sql.Date 에러가 발생했습니다. 이 에러가 발생한 원인과 해결과정에 대해서 다루어보겠습니다.


원인 파악 (Diagnosis)

먼저 상황을 재현해보도록 하겠습니다. 아래와 같이 LocalDate 타입을 가진 필드를 선언한 엔티티를 이용해 데이터베이스에서 데이터를 가져오는 과정에서 에러가 발생했습니다. 날짜가 포함된 컬럼에 '0000-00-00' 데이터를 가진 튜플이 있었는데, 이 튜블을 데이터베이스에서 조회해 클래스로 변환하는 과정에서 에러가 발생했습니다.

@Entity(name = "period_entity")
public class PeriodEntity {
    @Id
    Long id;
    Date startDate;
    Date endDate;
}


해결 과정 (Solution)

1. zeroDateTimeBehavior=convertToNull

구글링으로 해결방법을 검색한 결과, 대표적인 해결방법은 zeroDateTimeBehavior=convertToNull 를 다음과 같이 JDBC URL에 추가하는 것이었습니다. 이렇게 하면 데이터가 0000-00-00 값일때 JDBC 가 null을 대신 반환하도록 합니다. 이 방법은 간단하고 명확하게 에러를 해결하지만, 단점도 명확합니다. 이 방법은 모든 데이터베이스에 적용되는 것도 아니며, 0000-00-00 이라는 값을 null이 아닌 최소 시간으로 취급하는 경우에는 사용할 수 없습니다. 

 

2. AttributeConvert

제가 선택한 방법은 직접 변환과정을 설정하는 것이었습니다. AttributeConvert는 데이터베이스에서 읽은 데이터를 엔티티 필드와 데이터베이스 값 사이를 변환하는데 사용되는 인터페이스입니다. 아래와 같이 0000-00-00 값 일때 최소 값으로 변경함으로써 에러를 해결할 수 있습니다. 이 방법은 zeroDateTimeBehavior=convertToNull 와 달리 모든 데이터베이스에 적용할 수 있으며, 원하는 값으로 변경하 할 수 있습니다. 다음은 0000-00-00 값을 최소 시간으로 변경하는 코드입니다.

@Converter(autoApply = true)
public class ZeroDateConverter implements AttributeConverter<LocalDate,String> {

    public static final String ZERO_DATE_STRING = "0000-00-00";

    @Override
    public String convertToDatabaseColumn(LocalDate attribute) {
        return attribute.format(DateTimeFormatter.ISO_DATE_TIME);
    }

    @Override
    public LocalDate convertToEntityAttribute(String dbData) {
        if(dbData==null || dbData.equals(ZERO_DATE_STRING)){
            return LocalDate.MIN;
        }
        return LocalDate.parse(dbData,DateTimeFormatter.ISO_DATE_TIME);
    }
}


결론 (Conclusion)

지금까지 JPA를 사용하는 과정에서 0000-00-00 값을 변환하는 에러를 확인하고 이를 해결하는 과정을 살펴보았습니다. 저는 회사에서 기존에 운영하던 개발 데이터베이스에 접근하는 과정에서 위와 같은 에러가 발생했습니다. 만약 혼자만 사용하는 데이터베이스라면 0000-00-00 값을 파싱가능한 다른 값으로 변경하는 것도 좋겠지만, 하나의 디비에 여러 서비스가 물려있는 경우에는 적용할 수 없기에 위와 같은 솔루션을 사용하게 되었습니다.

 

 

+ Recent posts