Java/Spring

[Spring] org.springframework.orm.jpa.JpaSystemException: Error attempting to apply AttributeConverter

SeungbeomKim 2024. 9. 26. 18:34

 

오늘은 에러 로그 관련해서 포스팅을 진행하려고 합니다.

 

개발 도중에 발생한 Error log인데, Error log를 먼저 보여드리고 분석한 내용에 대해 설명드리겠습니다.

 

Error log

org.springframework.orm.jpa.JpaSystemException: Error attempting to apply AttributeConverter; nested exception is javax.persistence.PersistenceException: Error attempting to apply AttributeConverter
	at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:418)
	at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:227)
	at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:436)
	at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59)
	at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
	at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:131)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208)
	at com.sun.proxy.$Proxy212.save(Unknown Source)
	at com.piolink.cc.app.alarm.service.AbstractAlarmService.saveAlarmNow(AbstractAlarmService.java:123)
	at com.piolink.cc.app.alarm.service.AbstractAlarmService.saveAlarmNow(AbstractAlarmService.java:123)
	at com.piolink.cc.app.alarm.service.AbstractAlarmService.saveAlarm(AbstractAlarmService.java:111)
	at com.piolink.cc.app.alarm.service.IpConflictAlarmService.alarm(IpConflictAlarmService.java:45)
	at com.piolink.cc.app.ndm.service.NdmService.conflict(NdmService.java:226)
	at com.piolink.cc.app.ndm.service.NdmService$$FastClassBySpringCGLIB$$cb7d533e.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:651)
	at com.piolink.cc.app.ndm.service.NdmService$$EnhancerBySpringCGLIB$$e851a393.conflict(<generated>)
	at com.piolink.cc.app.ndm.service.NdmRouteService.route(NdmRouteService.java:61)
	at com.piolink.cc.app.ndm.service.NdmRouteService.event(NdmRouteService.java:38)
	...

 

간략히 읽어봤을 때, AttributeConverter를 적용하려고 시도했지만 실패하게 돼서 발생한 에러라는 것을 확인할 수 있습니다.

 

이제 여기에서 Converter라는 개념이 나왔는데, 무엇인지 간단히 짚고 넘어가도록 하겠습니다.

 

@Converter

  • Entity에 Data를 변환하여 DB에 저장할 수 있게 해주는 annotation
  • Convert를 적용할 필드에 @Convert 어노테이션과 만들어준 Convert class를 지정해 주면 됩니다

 

Converter 생성 방법

  • AttributerConverter의 convertToDatabaseColumn, convertToEntityAttribute 메서드를 override 해주어야 합니다.
  • Entity ↔ DB 간의 데이터 변환이 가능해집니다.

 

Example

@Converter(autoApply = true) // global 설정
public static class DeviceTypeConverter implements AttributeConverter<DeviceType, Integer> {

    @Override
    public Integer convertToDatabaseColumn(DeviceType deviceType) {
        return deviceType.getType();
    }

    @Override
    public DeviceType convertToEntityAttribute(Integer deviceType) {
        return DeviceType.getType(deviceType);
    }
}

 

다음과 같은 컨버터를 어떠한 필드에 적용할 때, 변환이 실패해서 Error attempting to apply AttributeConverter 에러가 발생하게 됩니다.

 

관련 클래스를 간단히 살펴보도록 하겠습니다.

 

ValueBinder

  • Object -> DB로 Data 변환 

 

ValueExtractor

  • DB -> Object로 데이터 변환

위의 데이터 변환 메서드를 참고했을 때, 해당 메서드의 try ~ catch 문에서 엔티티의 값, 도메인 값에 Null이 들어가거나 변환이 불가능하면, 발생하는 Error입니다.  

 

해당 Error를 해결하기 위해, DB <-> Entity의 변환 과정에서 null 처리를 해주면 간단히 해결할 수 있게 됩니다.