JDBCTemplate : GoF 디자인 패턴 중 템플릿 메소드 패턴이 적용된 클래스
템플릿 메소드 패턴: 복잡하고 반복되는 알고리즘을 캡슐화하여 재사용하는 패턴
JDBC처럼 코딩 순서가 정형화된 기술에서 유용하게 사용할 수 있음
반복되는 DB 연동 로직은 JdbcTemplate 클래스의 템플릿 메소드가 제공하고, 개발자는 달라지는 SQL 구문과 설정값만 신경 쓰면 됨

JDBCTemplate : JDBC의 반복적인 코드를 제거하기 위해 제공하는 클래스
DAO 클래스에서는 JDBCTemplate 클래스가 제공하는 템플릿 메소드를 호출하여 DB 연동을 간단하게 처리함
JDBCTemplate 클래스는 내부적으로 JDBC API를 이용하여 실제 DB 연동 작업을 처리함
개발자는 JDBCTemplate 클래스가 어떻게 JDBC API를 이용하는지는 신경 쓸 필요가 없음
public class PieceDao() {
public Connection connection() {
Connection connection = null;
String server = "localhost:13306";
String database = "db_name";
String option = "?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf8";
String userName = "root";
String password = "root";
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
System.err.println(" !! JDBC Driver load 오류: " + e.getMessage());
e.printStackTrace();
}
try {
connection = DriverManager
.getConnection("jdbc:mysql://" + server + "/" + database + option, userName,
password);
} catch (SQLException e) {
System.err.println("연결 오류:" + e.getMessage());
e.printStackTrace();
}
return connection;
}
public void closeConnection(Connection connection) {
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
System.err.println("con 오류:" + e.getMessage());
}
}
protected void disconnect(PreparedStatement preparedStatement, ResultSet resultSet) throws SQLException {
if (!Objects.isNull(preparedStatement)) {
preparedStatement.close();
}
if (!Objects.isNull(resultSet)) {
resultSet.close();
}
}
public void addPieces(final int boardId, final Map<Position, Piece> pieces) throws SQLException {
Connection connection = connection();
PreparedStatement preparedStatement = null;
connection.setAutoCommit(false);
try {
for (Piece piece : pieces.values()) {
if (Objects.isNull(piece)) {
continue;
}
String query = "INSERT INTO piece(board_id, piece_position, piece_symbol) VALUES (?, ?, ?)";
preparedStatement = connection.prepareStatement(query);
addPieceDataInit(boardId, preparedStatement, piece);
preparedStatement.executeUpdate();
preparedStatement.close();
}
} finally {
disconnect(preparedStatement, null);
closeConnection(connection);
}
}
private void addPieceDataInit(final int boardId, final PreparedStatement preparedStatement,
final Piece piece) throws SQLException {
preparedStatement.setInt(1, boardId);
preparedStatement.setString(2, piece.position().changedPositionToString());
preparedStatement.setString(3, piece.symbol());
}
}
이 코드는 체스게임에서 말들의 정보를 저장하기 위한 코드
JDBC로 작성하면 위와 같이 길어지는 부분
이러한 코드를 JdbcTemplate으로 바꾸면 아래와 같아짐
@Repository
public class PieceDao {
private final JdbcTemplate jdbcTemplate;
public PieceDao(final JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public void addPieces(long boardId, Map<Position, Piece> pieces) {
String sql = "INSERT INTO piece(board_id, piece_position, piece_symbol) VALUES (?, ?, ?)";
List<Position> positions = new ArrayList<>(pieces.keySet());
jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement preparedStatement, int i) throws SQLException {
Piece piece = pieces.get(positions.get(i));
preparedStatement.setInt(1, boardId);
preparedStatement.setString(2, piece.position().changedPositionToString());
preparedStatement.setString(3, piece.symbol());
}
@Override
public int getBatchSize() {
return pieces.size();
}
});
}
}
BatchUpdate를 적용. 불필요한 처리작업이 사라져 코드가 간결Connection으로 연결하기PreparedStatement로 실행하기resultSet을 이용하여 결과 처리하기Connection 닫아주기