[Spring Data Jdbc] 코틀린에서 wither를 인식하지 못하는 문제
2020. 12. 6. 22:58ㆍJava
자바에서 Spring Data Jdbc 사용할 때 setter 없이 id를 채워주려면(populate) 다음과 같이 Wither를 만들어주면 된다.
@Getter
@RequriedArgsConstructor
public class Menu {
@Id
private final Long id;
private final String name;
private final double price;
public Menu(String name, double price) {
this(null, name, price);
}
public Menu withId(Long id) {
return new Menu(id, name, price);
}
}
코틀린에서도 다음과 같이 wither를 만들어주면 작동하겠거니 했다.
class Menu(
val name: String,
val price: Double,
@Id val id: Long? = null
) {
fun withId(id: Long): Menu = Menu(name, price, id)
}
하지만 다음과 같이 id를 set할 수 없다는 에러가 난다.java.lang.UnsupportedOperationException: No accessor to set property @org.springframework.data.annotation.Id()
디버깅을 하다보니 아래와 같은 코드를 만났다.
@Override
public void setProperty(PersistentProperty<?> property, @Nullable Object value) {
if (!property.isImmutable() || property.getWither() != null || KotlinDetector.isKotlinType(owner.getType())) {
delegate.setProperty(property, value);
this.bean = delegate.getBean();
return;
}
주목해야할 부분은 property.getWither()
의 결과가 null이 나온다는 점이었다. Wither를 인식하지 못하는 것 같아서 프로퍼티에 wither를 찾아서 넣어주는 부분을 찾아봤다.
private static Optional<Method> findWither(TypeInformation<?> owner, String propertyName, Class<?> rawType) {
AtomicReference<Method> resultHolder = new AtomicReference<>();
String methodName = String.format("with%s", StringUtils.capitalize(propertyName));
ReflectionUtils.doWithMethods(owner.getType(), it -> {
if (owner.isAssignableFrom(owner.getReturnType(it))) {
resultHolder.set(it);
}
}, it -> isMethodWithSingleParameterOfType(it, methodName, rawType));
Method method = resultHolder.get();
return method != null ? Optional.of(method) : Optional.empty();
}
private static boolean isMethodWithSingleParameterOfType(Method method, String name, Class<?> type) {
return method.getParameterCount() == 1 //
&& method.getName().equals(name) //
&& method.getParameterTypes()[0].equals(type);
}
가장 아랫줄에서 method.getParameterTypes()[0].equals(type)
의 결과가 false로 나오는 것을 확인했다.
코틀린의 Long은 non-nullable하기 때문에 자바에서는 long으로 번역되고, 코틀린의 Long?은 nullable하기 떄문에 자바에서는 java.lang.Long으로 번역된다.
이 때 long.equals(Long)
의 결과가 false기 때문에 같은 타입으로 인식하지 못해서 Wither를 인식하지 못하는 문제였다.
따라서, 아래와 같이 필드 타입과 동일하게 파라미터 타입을 맞춰주니 해결되었다(돌이켜보면 당연한 건데...)
class Menu(
val name: String,
val price: Double,
@Id val id: Long? = null
) {
fun withId(id: Long?): Menu = Menu(name, price, id)
}
'Java' 카테고리의 다른 글
[객체지향] 캡슐화 - 객체의 값을 꺼내지 말고 메시지를 던져라 (0) | 2021.01.24 |
---|---|
[Spring] Transactional Rollback (0) | 2020.08.03 |
[Jackson] Jackson 파싱 전략(불변 객체 활용) (1) | 2020.07.12 |
[Spring Data JDBC] 라이프사이클 이벤트와 콜백(LifeCycle Events & Callback) (1) | 2020.07.11 |
[WebClient] @RestClientTest를 WebFlux에서 사용하기 (0) | 2020.07.11 |