이전 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 를 보면
- 컴파일을 통해 DB 컬럼의 데이터 타입을 알아냄
- 파라미터 값과 insert 할 컬럼 값을 매칭
- insert 실행
이런 식으로 진행되는 것 같다. (자세히는 모르겠지만, 확인한 바로는)
문제는 컴파일 하는 시점에 DB 컬럼 값의 데이터 타입을 가져와서 java.sql.Types 의 값을 맞추는데, Maria DB 타입에는 int 는 있지만, long 은 없다.
그렇다보니 DB 컬럼에는 int 로 되어 있기에, 컴파일 할 때 reg_ip 가 int value 가 되는 것이고.. 최대 값을 지나가버린 long value 는 overflow 가 나서 그대로 - 값...
이 시점에서 내가 취할 수 있는 선택지는 DB 컬럼 값을 바꾸거나, 이전 jdbcTemplate 을 사용하는 것인데 DB 를 함부로 바꿀 순 없고, 소스를 바꾸는 게 리소스 적으로 좋다고 판단하여 소스를 변경하여서 일단락 되었다.
역시 잘 알고 써야 한다.. ㅋㅋ
'개발 관련 > java' 카테고리의 다른 글
캡슐화(encapsulation) (0) | 2022.07.14 |
---|---|
Transactional 정리 (0) | 2022.07.14 |
멀티쓰레드에서 Thread-Safe 방법 (0) | 2022.07.14 |
orElse 와 orElseGet 무슨 차이가 있을까? (0) | 2022.07.14 |
DB Insert 시 자동생성된 id 를 알아내기 (0) | 2022.07.14 |