영호
Jdbc와 Jdbc template비교 본문
기존 JDBC의 문제점
반복 코드가 너무 많다. 여기서 반복하는 작업은 아래와 같다.
- DB연결을 위한 자원 명시하기
- 파라미터 바인딩
- 마지막에 자원 반납하기
- 이 부분은 try with resource로 어느정도 해결이 가능하다.
- 결과 바인딩
JDBC는 아래 코드 처럼 디비 연결, 파라미터 바인딩, 결과 변환 등등을 다른 쿼리를 날리는 메서드에서도 반복해야 한다.
public List<Long> findAllGameRooms() {
String selectQuery = "SELECT game_room_id, status FROM game_room";
try (Connection connection = dbConnection.getConnection()) {
PreparedStatement preparedStatement = connection.prepareStatement(selectQuery);
ResultSet resultSet = preparedStatement.executeQuery();
List<Long> roomIds = new ArrayList<>();
while (resultSet.next()) {
roomIds.add(resultSet.getLong("game_room_id"));
}
return roomIds;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public boolean saveChessBoard(Map<Position, Piece> board, Side currentTurn, Long roomId) {
final String saveQuery = "INSERT INTO pieces(piece_type, side, piece_rank, piece_file, game_room_id_fk) VALUES(?,?,?,?,?)";
try (Connection connection = dbConnection.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(saveQuery);) {
try {
for (Map.Entry<Position, Piece> pieces : board.entrySet()) {
File file = pieces.getKey().getFile();
Rank rank = pieces.getKey().getRank();
PieceType pieceType = pieces.getValue().getPieceType();
Side pieceSide = pieces.getValue().getSide();
preparedStatement.setString(1, pieceType.name());
preparedStatement.setString(2, pieceSide.name());
preparedStatement.setString(3, rank.getText());
preparedStatement.setString(4, file.getText());
preparedStatement.setLong(5, roomId);
preparedStatement.executeUpdate();
}
connection.commit();
return true;
} catch (SQLException sqlException) {
connection.rollback();
throw new RuntimeException(sqlException);
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public Connection getConnection() {
try {
Connection connection = DriverManager.getConnection("jdbc:mysql://" + SERVER + "/" + DATABASE + OPTION, USERNAME, PASSWORD);
connection.setAutoCommit(false);
return connection;
} catch (final SQLException e) {
System.err.println("DB 연결 오류:" + e.getMessage());
e.printStackTrace();
return null;
}
}
JDBC Template
바로 코드를 보면서 비교해보겠다. 코드는 공식문서에서 가져왔다.
DB연결
@Repository
public class JdbcCorporateEventDao implements CorporateEventDao {
private JdbcTemplate jdbcTemplate;
@Autowired
public JdbcCorporateEventDao(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
}
DB연결이 끝났다.이제 jdbcTemplate을 이용해 쿼리를 날릴 수 있다.
DataSouce는 디비 연결을 위한 정보(username, password등등)를 관리하는 객체다.
파라미터 바인딩
String query = "insert into t_actor (first_name, last_name) values (?, ?)";
this.jdbcTemplate.update(query, "Leonor", "Watling");
기존 JDBC코드보다 파라미터 바인딩이 훨씬 깔끔해졌다.
그러나, 개인적으로 위 방식은 개발자가 파라미터를 잘못 바인딩할 위험이 있다고 생각한다. 그래서 Spring에서도 NamedParameterJdbcTemplate을 제공한다.
코드만 간단하게 보자면 아래와 같다.
String sql = "select count(*) from T_ACTOR where first_name = :first_name";
SqlParameterSource namedParameters = new MapSqlParameterSource("first_name", firstName);
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
이런 식으로 명시적으로 파라미터를 바인딩 해 실수를 줄일 수 있다. 자세한 내용은 공식문서를 보자
결과 바인딩
private final RowMapper<Actor> actorRowMapper = (resultSet, rowNum) -> {
Actor actor = new Actor();
actor.setFirstName(resultSet.getString("first_name"));
actor.setLastName(resultSet.getString("last_name"));
return actor;
};
public List<Actor> findAllActors() {
return this.jdbcTemplate.query("select first_name, last_name from t_actor", actorRowMapper);
}
- RowMapper를 이용해 조회 결과를 기반으로 객체를 만들 수 있다.
- 여기서 resultSet은 Jdbc와 마찬가지로 조회된 결과를 의미하고, rowNum은 조회된 데이터의 row개수를 의미한다.
- query는 여러 건 조회, queryForObject를 사용하면 단건 조회가 가능해서 return이 List가 아닌 Actor가 된다.
- 공식문서를 보면 @FunctionalInterface가 붙어있는데 실제 코드를 보면 해당 어노테이션은 없다.
- RowMapper는 메서드가 하나만 존재하는 인터페이스라 람다 전달이 가능하다.
정리
- JDBC Template은 JDBC의 반복코드를 상당부분 줄여줘 개발 생산성을 높여주고, 가독성도 높아진다.
- 추가적으로 insert를 손쉽게 지원해주는 SimpleJdbcInsert도 존재한다.
- 사용해본적은 없지만 SimpleJdbcCall도 존재한다.
궁금한 점
- 공식문서에는 JdbcTemplate을 초기화 할 때 DataSource를 주입받는다. 그러나 Spring boot를 사용하면 명시적으로 주입 받을 필요가 없다.
- 이유를 찾아보니 application.yml에 정의한 db관련 리소스를 바탕으로 Spring boot가 자동으로 DataSource빈을 생성한다.
- 이후, JdbcTemplate에 주입해주기 때문에 별도로 DataSource객체를 생성하려고 신경쓰지 않아도 된다.
private final JdbcTemplate jdbcTemplate;
public GameDao(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
'Spring' 카테고리의 다른 글
ResponseEntity의 created(URI)는 뭘까? (feat.httpCode 201) (0) | 2023.04.24 |
---|---|
공식문서로 알아보는 IoC container (0) | 2023.04.20 |
[Spring JDBC] SimpleJdbcInsert로 쉽게 INSERT하기 (0) | 2023.04.12 |
[Spring] 컴포넌트 스캔 (0) | 2022.12.26 |
[Spring] BeanFactory, ApplicationContext (0) | 2022.12.23 |
Comments