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
닫아주기