본문 바로가기
개발 관련/java

SimpleJdbcInsert 문제

by lazysnack 2022. 7. 14.

이전 jdbcTemplate 를 설명하면서 simpleJdbcInsert 에 대한 칭찬(?)을 했었다.

@Override
public Long saveLoginUI(final LoginVO loginVO) {
    KeyHolder keyHolder = new GeneratedKeyHolder();
    jdbcTemplate.update(new PreparedStatementCreator() {
         @Override
         public PreparedStatement createPreparedStatement(final Connection connection) throws SQLException {
                PreparedStatement statement = connection.prepareStatement("insert into login_ui " +
                        "(ui_name, img_path, use_yn, reg_id, reg_dt, reg_ip) " +
                        "values (?,?,?,?,?,?)", new String[]{"id"});
                statement.setString(1, loginVO.getName());
                statement.setString(2, loginVO.imgPath());
                statement.setString(3, loginVO.isUse() == true ? "Y" : "N");
                statement.setLong(4, loginVO.getRegInfo().getId());
                statement.setTimestamp(5, Timestamp.valueOf(loginVO.getRegInfo().getDate()));
                statement.setLong(6, inetConverter.convertToDatabaseColumn(loginUIData.getRegInfo().getIp()));

                return statement;
            }
        }, keyHolder);
        loginUIData.setId(keyHolder.getKey().longValue());
        return loginUIData.getId();
 }

이렇게 작성했던 코드를

@Override
public Long saveLoginUI(final LoginVO loginVO) {

    SqlParameterSource sqlParameterSource = new MapSqlParameterSource()
            .addValue("ui_name", loginVO.getName())
            .addValue("img_path", loginVO.getImgPath())
            .addValue("use_yn", loginVO.isUse() == true ? "Y" : "N")
            .addValue("reg_id", loginVO.getRegInfo().getId())
            .addValue("reg_dt", loginVO.getRegInfo().getDate())
            .addValue("reg_ip", inetConverter.convertToDatabaseColumn(loginVO.getRegInfo().getIp()));

    Number id = simpleJdbcInsert.executeAndReturnKey(sqlParameterSource);
    loginUIData.setId(id.longValue());
    return loginUIData.getId();
}

이런식으로 간편하게 만들어 줫었으니 말이다.

테스트 코드에서도 문제 없고, 로컬에서도 잘 동작하길래 개발 서버에 올렸더니...

오늘 한 방 먹었다...

Caused by: java.sql.SQLException: Out of range value for column 'reg_ip' at row 1
Query is: INSERT INTO login_ui (ui_name, img_path, use_yn, reg_id, reg_dt, reg_ip, upd_id, upd_dt, upd_ip) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?), parameters ['test','img_path','N',1522
909,'2019-12-09 15:40:04.143',-1218290558,<null>,<null>,<null>]
    at org.mariadb.jdbc.internal.util.LogQueryTool.exceptionWithQuery(LogQueryTool.java:153)
    at org.mariadb.jdbc.internal.protocol.AbstractQueryProtocol.executeQuery(AbstractQueryProtocol.java:254)
    at org.mariadb.jdbc.MariaDbPreparedStatementClient.executeInternal(MariaDbPreparedStatementClient.java:209)
    ... 85 common frames omitted

?????

reg_ip 에 왜 -1218290558 라는 값이...?

Long 으로 넘어가는 건 확인했다. 그런데 왜 - 값이 나오는걸까?? 하다가 long -> 숫자.. 어? 생각해보니 overflow !!

ip를 DB 에 저장할 때, 그대로 저장하지 않고, inetConvert 를 통해 long 값으로 저장하는데, 로컬에서는 테스트 할 때 127.0.0.1 은 int 로 변경해도 int의 최대 값 범위 안인데, 다른 아이피의 경우 int 의 최대 값을 넘어가기에 overflow 가 발생하여 - 값이 되는 것이었다.

그렇다면 왜 long 로 설정한 값이 int 로 강제 형변환이 되어 저장이 되었던 걸까?

그래서 디컴파일을 통해 찾아봤다.

SimpleJdbcInsert 의 executeAndReturnKey 를 보면

  1. 컴파일을 통해 DB 컬럼의 데이터 타입을 알아냄
  2. 파라미터 값과 insert 할 컬럼 값을 매칭
  3. insert 실행

이런 식으로 진행되는 것 같다. (자세히는 모르겠지만, 확인한 바로는)

문제는 컴파일 하는 시점에 DB 컬럼 값의 데이터 타입을 가져와서 java.sql.Types 의 값을 맞추는데, Maria DB 타입에는 int 는 있지만, long 은 없다.

그렇다보니 DB 컬럼에는 int 로 되어 있기에, 컴파일 할 때 reg_ip 가 int value 가 되는 것이고.. 최대 값을 지나가버린 long value 는 overflow 가 나서 그대로 - 값...

이 시점에서 내가 취할 수 있는 선택지는 DB 컬럼 값을 바꾸거나, 이전 jdbcTemplate 을 사용하는 것인데 DB 를 함부로 바꿀 순 없고, 소스를 바꾸는 게 리소스 적으로 좋다고 판단하여 소스를 변경하여서 일단락 되었다.

역시 잘 알고 써야 한다.. ㅋㅋ