org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: org.apache.ibatis.reflection.ReflectionException: Error instantiating class java.lang.Long with invalid types () or values (). Cause: java.lang.NoSuchMethodException: java.lang.Long.<init>()
### The error may exist in gracefulsoul/mybatis/resouces/mapper/CustomerMapper.xml
### The error may involve gracefulsoul.mybatis.resouces.mapper.CustomerMapper.getCustomer
### The error occurred while handling results
### SQL: SELECT * FROM customer WHERE id = ?
### Cause: org.apache.ibatis.reflection.ReflectionException: Error instantiating class java.lang.Long with invalid types () or values (). Cause: java.lang.NoSuchMethodException: java.lang.Long.<init>()
해당 쿼리를 수행하는 API를 호출하게 되면 갑자기 이런 오류가 발생한다.
오류 내용을 확인하면 MyBatis에서 Reflection을 사용하는데, java.lang.Long 클래스의 Default Constructor(기본 생성자)가 존재하지 않는다고 오류가 발생하였다.
원인 분석
Java Docs - java.lang.Long
/* line 932 */
* The value of the {@code Long}.
* @serial
private final long value;
* Constructs a newly allocated {@code Long} object that
* represents the specified {@code long} argument.
* @param value the value to be represented by the
* {@code Long} object.
public Long(long value) {
this.value = value;
* Constructs a newly allocated {@code Long} object that
* represents the {@code long} value indicated by the
* {@code String} parameter. The string is converted to a
* {@code long} value in exactly the manner used by the
* {@code parseLong} method for radix 10.
* @param s the {@code String} to be converted to a
* {@code Long}.
* @throws NumberFormatException if the {@code String} does not
* contain a parsable {@code long}.
* @see java.lang.Long#parseLong(java.lang.String, int)
public Long(String s) throws NumberFormatException {
this.value = parseLong(s, 10);
/* line 967 */
위의 Java Docs 내 java.lang.Long 소스코드를 보면 Default Constructor가 존재하지 않는 것을 확인 할 수 있다.
MyBatis에서는 Reflection을 이용하여 동적인 파라미터 초기화를 수행하므로, 기본 생성자가 없으면 객체 초기화가 불가능하기 때문에 당연히 오류가 발생하게 된다.
그렇기 때문에 MyBatis에서 java.lang.Long을 parameterType으로 명시할 경우, 해당 타입의 기본 생성자가 없으므로 오류가 발생한다.
오류 해결
Mapper
<!-- ... -->
<select id="getCustomer">
SELECT * FROM customer
WHERE id = #{id}
</select>
<!-- ... -->
해결 방법은 Mapper 내 getCustomer에 parameterType을 명시하지 않도록 처리하였다.
이렇게 실제 주입한 파라미터가 java.lang.Long이어도 오류가 발생하지 않는 이유는 AutoBoxing과 UnBoxing에 의해 타입 유추가 되어 위의 Exception이 발생하지 않는 것이다.