MyBatis java.lang.NoSuchMethodException: java.lang.Long.() 원인과 해결

업데이트:

오류 발생

  • Mapper
  <!-- ... -->
  <select id="getCustomer" parameterType="java.lang.Long">
    SELECT * FROM customer
    WHERE id = #{id}
  </select>
  <!-- ... -->
  • 간단히 주어진 고객 id를 이용하여, 해당 고객의 정보를 가져오는 쿼리를 Mapper에 생성하였다.

  • Console Log

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이 발생하지 않는 것이다.

댓글남기기