반응형

DBUtils에서 number 타입의 컬럼이 int형으로 안넘어올때..


데이터베이스의 컬럼이 NUMBER 타입인데 VO객체의 int형 setter를 통해 그 값이 안넘어 오는 경우가 있습니다


데이터베이스 테이블 스크립트

CREATE TABLE user_t (

    user_id VARCHAR2(12) PRIMARY KEY NOT NULL,

    user_point NUMBER(8)

);


데이터베이스의 값을 저장하는 VO 객체

public class UserVO {

    String user_id;

    int user_point;


    public void setUser_id(String user_id) { this.user_id = user_id; }

    public void setUser_point(int user_point) { this.user_point = user_point; }

    public String getUser_id() { return user_id; }

    public int getUser_point() { return user_point; }

}



JSP

...

ResultSetHandler rsh = new BeanListHandler(UserVO.class);

QueryRunner qr = new QueryRunner();

List list = (List)qr.query(conn, "SELECT user_id, user_point FROM user_t WHERE user_id = ?", new String[]{"unicorn"}, rsh);

...


간단히 위와 같이 코딩을 하면 다음과 같은 에러 메세지가 납니다

java.sql.SQLException Cannot set user_point : argument type mismatch Query

user_point 컬럼이랑 먼지 모르지만 아규먼트랑 type이 맞지 않는다는 말 같군요


DBUtils 받아서 차근차근 소스를 보다보니..

org.apache.commons.dbutils.BasicRowProcessor.java

private void callSetter(
    Object target,
    PropertyDescriptor prop,
    Object value)
    throws SQLException {


    Method setter = prop.getWriteMethod();
    if (setter == null) {
        return;
    }

    Class[] params = setter.getParameterTypes();
    try {

        // Don't call setter if the value object isn't the right type
        if (this.isCompatibleType(value, params[0]))
            setter.invoke(target, new Object[] { value });  //--


    } catch (IllegalArgumentException e) {
        throw new SQLException(
            "Cannot set " + prop.getName() + ": " + e.getMessage());

    } catch (IllegalAccessException e) {
        throw new SQLException(
            "Cannot set " + prop.getName() + ": " + e.getMessage());

    } catch (InvocationTargetException e) {
        throw new SQLException(
            "Cannot set " + prop.getName() + ": " + e.getMessage());
    }
}


의 setter.invoke 에서 IllegalArgumentException 가 throw 되고 있었습니다

즉 값에 해당하는 setter 함수를 찾다가 setUser_point(int user_point) 가 있음에도 불고하고 적당한 것이 없어서 Exception을 던지고 있는 실정입니다

원인은 value 때문이었는데, 이는

value = rs.getObject(i+1)

와 같이 resultset에서 받아온 값입니다

invoke 함수에 두번째 파라미터로 Object형태의 객체형태로 넘겨주어야 하는데 이넘은 Integer형이 아닌것 같았습니다

Integer.class.isInstance(value) 로 값을 찍어보니 역시나 false가 리턴되었습니다


그래서 다음과 같이 약간 수정하였습니다


private void callSetter(
    Object target,
    PropertyDescriptor prop,
    Object value)
    throws SQLException {


    Method setter = prop.getWriteMethod();
    if (setter == null) {
        return;
    }

    Class[] params = setter.getParameterTypes();
    try {

        // Don't call setter if the value object isn't the right type


        if (params[0].equals(Integer.TYPE)) {
            value = new Integer(value.toString());  //-- ②
       }


        if (this.isCompatibleType(value, params[0]))
            setter.invoke(target, new Object[] { value });  //-- ①


    } catch (IllegalArgumentException e) {
        throw new SQLException(
            "Cannot set " + prop.getName() + ": " + e.getMessage());

    } catch (IllegalAccessException e) {
        throw new SQLException(
            "Cannot set " + prop.getName() + ": " + e.getMessage());

    } catch (InvocationTargetException e) {
        throw new SQLException(
            "Cannot set " + prop.getName() + ": " + e.getMessage());
    }
}


과 같이 명시적으로 Integer 타입일때 Integer형태를 만들어 주었습니다

MySQL과 Oracle 두가지 테스트해보았는데, MySQL에서는 발생하지 않았지만 Oracle에서는 위와같은 문제가 발견되었습니다

아마도 M$SQL에서도 동일한 문제가 발생할것 같습니다

소스는 동일한데 어디선 되고 안되고를 보니 JDBC영향일것으로 추측이 되는데, JDBC 소스를 보아도 별 특별한데는 아직 찾지 못했습니다


사실 DBUtils는 잘쓰면 무척 편합니다

하지만 많이 좋아졌다고는 하나 reflect에 대한 비용 없잖아 들겁니다

DBUtils는 그 자체로 괜찮지만 아마 사용하다보면 소스에 손을 데야될겁니다

한글 인코딩, 디코딩이나 쿼리등을 DBUtils에 심어놓으면 코딩은 아마 더 줄어들겁니다

Posted by 1010