이전글을 기반으로 하는 리팩토링 기록 입니다.
1. DbTemplate 리팩토링 하기
기존 DbTemplate이 update 메서드는 다음과 같다.
public Long executeUpdate(String sql, PreparedStatementSetter pss) {
Connection connection = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
connection = getConnection();
pstmt = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
pss.setParameters(pstmt);
pstmt.executeUpdate();
rs = pstmt.getGeneratedKeys();
if (rs.next()) {
return rs.getLong(1);
}
} catch (SQLException e) {
return -1L;
} finally {
DbCleaner.close(connection, pstmt, rs, dataSource);
}
return -1L;
}
이 메서드를 호출할때 인자로 PreparedStatementSetter를 호출하고 있는데, 이를 사용하려면 외부에서 다음과 같이 넘겨주어야 한다.
PreparedStatementSetter pss = new PreparedStatementSetter() {
@Override
public void setParameters(PreparedStatement pstmt) throws SQLException {
pstmt.setString(1, user.getUserId());
pstmt.setString(2, user.getPassword());
pstmt.setString(3, user.getName());
pstmt.setString(4, user.getEmail());
}
};
위와 같은 함수를 executeUpdate(sql, pss)의 메서드로 전달하는 것 인다. (더 리팩토링 하면 람다식을 사용할 수 있을것 같다)
하지만 위와 같은 과정을 불편하다. 항상 setParameters의 메서드를 오버라이딩 하여 넘겨줘야 할까?
그냥 인자를 넘기면 되게할수 없을까?
다음과 같이 리팩토링 할 수 있다.
public Long executeUpdate(String sql, PreparedStatementSetter pss) {
Connection connection = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
connection = getConnection();
pstmt = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
pss.setParameters(pstmt);
pstmt.executeUpdate();
rs = pstmt.getGeneratedKeys();
if (rs.next()) {
return rs.getLong(1);
}
} catch (SQLException e) {
return -1L;
} finally {
DbCleaner.close(connection, pstmt, rs, dataSource);
}
return -1L;
}
// 새롭게 추가된 부분!!!!!!!!
public Long executeUpdate(String sql, Object... parameters) {
return executeUpdate(sql, getPreparedStatementSetter(parameters));
}
private PreparedStatementSetter getPreparedStatementSetter(Object[] parameters) {
return new PreparedStatementSetter() {
@Override
public void setParameters(PreparedStatement pstmt) throws SQLException {
for (int i = 0; i < parameters.length; i++) {
pstmt.setObject(i + 1, parameters[i]);
}
}
};
}
우선 새롭게 추가한 executeUpdate는 인자로 Object... 을 받는다. 잘 보면 가변인자 문법을 사용하고 있다.
즉 지정할 파라미터들을 가변적으로 받게 된다.
이렇게 받은 파라미터들을 getPreparedStatementSetter를 통해 pss로 변환하여 기존의 executeUpdate(sql, pss)의 인자로 전달하면 된다.
따라서 사용하는 입장에서는 다음과 같이 사용할 수 있다.
public Long save(User user) {
DbTemplate template = new DbTemplate(dataSource);
String SQL = "INSERT INTO user_info (user_id, password, name, email) VALUES (?, ?, ?, ?)";
Long saveId = template.executeUpdate(SQL, user.getUserId(), user.getPassword(), user.getName(), user.getEmail());
user.setId(saveId);
return saveId;
}
executeUpdate 에게 인자를 전달하게 변경 하였다!!
이런식으로 나머지 메서드 들도 리팩토링 진행하였습니다.
2. 전체 코드
- DbTemplate
public class DbTemplate {
private final DataSource dataSource;
public DbTemplate(DataSource dataSource) {
this.dataSource = dataSource;
}
private Connection getConnection() {
return DataSourceUtils.getConnection(dataSource);
}
public Long executeUpdate(String sql, PreparedStatementSetter pss) {
Connection connection = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
connection = getConnection();
pstmt = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
pss.setParameters(pstmt);
pstmt.executeUpdate();
rs = pstmt.getGeneratedKeys();
if (rs.next()) {
return rs.getLong(1);
}
} catch (SQLException e) {
return -1L;
} finally {
DbCleaner.close(connection, pstmt, rs, dataSource);
}
return -1L;
}
public Long executeUpdate(String sql, Object... parameters) {
return executeUpdate(sql, getPreparedStatementSetter(parameters));
}
public <T> T executeQuery(String sql, RowMapper<T> mapper, PreparedStatementSetter pss) {
Connection connection = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
connection = getConnection();
pstmt = connection.prepareStatement(sql);
pss.setParameters(pstmt);
rs = pstmt.executeQuery();
T value = mapper.rowMapper(rs);
if (value != null) return value;
} catch (SQLException e) {
return null;
} finally {
DbCleaner.close(connection, pstmt, rs, dataSource);
}
return null;
}
public <T> T executeQuery(String sql, RowMapper<T> mapper, Object... parameters) {
return executeQuery(sql, mapper, getPreparedStatementSetter(parameters));
}
private PreparedStatementSetter getPreparedStatementSetter(Object[] parameters) {
return new PreparedStatementSetter() {
@Override
public void setParameters(PreparedStatement pstmt) throws SQLException {
for (int i = 0; i < parameters.length; i++) {
pstmt.setObject(i + 1, parameters[i]);
}
}
};
}
public <T> List<T> list(String sql, RowMapper<T> mapper, PreparedStatementSetter pss) {
Connection connection = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
List<T> list = new ArrayList<>();
try {
connection = getConnection();
pstmt = connection.prepareStatement(sql);
pss.setParameters(pstmt);
rs = pstmt.executeQuery();
while (rs.next()) {
list.add(mapper.rowMapper(rs));
}
return list;
} catch (SQLException e) {
return null;
} finally {
DbCleaner.close(connection, pstmt, rs, dataSource);
}
}
public <T> List<T> list(String sql, RowMapper<T> mapper, Object... parameters) {
return list(sql, mapper, getPreparedStatementSetter(parameters));
}
}
- DbUserRepository
@Primary
@Repository
public class DbUserRepository implements UserRepository {
private final DataSource dataSource;
public DbUserRepository(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public Long save(User user) {
DbTemplate template = new DbTemplate(dataSource);
String SQL = "INSERT INTO user_info (user_id, password, name, email) VALUES (?, ?, ?, ?)";
Long saveId = template.executeUpdate(SQL, user.getUserId(), user.getPassword(), user.getName(), user.getEmail());
user.setId(saveId);
return saveId;
}
@Override
public Optional<User> findByUserId(String userId) {
RowMapper<User> mapper = new RowMapper<User>() {
@Override
public User rowMapper(ResultSet rs) throws SQLException {
while (rs.next()) {
User user = new User(rs.getString("user_id"), rs.getString("password"), rs.getString("name"), rs.getString("email"));
user.setId(rs.getLong("id"));
return user;
}
return null;
}
};
DbTemplate template = new DbTemplate(dataSource);
String SQL = "SELECT id, user_id, password, name, email FROM user_info WHERE user_id = (?)";
return Optional.ofNullable(template.executeQuery(SQL, mapper, userId));
}
@Override
public List<User> findAll() {
RowMapper<User> mapper = new RowMapper<User>() {
@Override
public User rowMapper(ResultSet rs) throws SQLException {
User user = new User(rs.getString("user_id"), rs.getString("password"), rs.getString("name"), rs.getString("email"));
user.setId(rs.getLong("id"));
return user;
}
};
DbTemplate template = new DbTemplate(dataSource);
String SQL = "SELECT id, user_id, password, name, email FROM user_info";
return template.list(SQL, mapper);
}
@Override
public boolean delete(String userId) {
DbTemplate template = new DbTemplate(dataSource);
String SQL = "DELETE FROM user_info WHERE user_id = (?)";
Long resultId = template.executeUpdate(SQL, userId);
if (resultId != -1) {
return true;
}
return false;
}
@Override
public boolean update(String userId, User updateParam) {
DbTemplate template = new DbTemplate(dataSource);
String SQL = "UPDATE user_info SET user_id = (?), password = (?), name = (?), email = (?) WHERE user_id = (?)";
Long resultId = template.executeUpdate(SQL, updateParam.getUserId(), updateParam.getPassword(), updateParam.getName(), updateParam.getEmail(), updateParam.getUserId());
if (resultId != -1) {
return true;
}
return false;
}
}
'BackEnd > JDBC' 카테고리의 다른 글
[JDBC] DAO 리팩토링 2 - 하나의 Template 으로 이동 (0) | 2022.03.13 |
---|---|
[JDBC] DAO 리팩토링 1 - 개별 Template 만들기 (0) | 2022.03.12 |
[JDBC] 순수 JDBC CRUD 코드 (0) | 2022.03.12 |
[JDBC] PrepareStatement에서 TimeStamp, LocalDateTime 사용하기 (0) | 2022.03.06 |
[JDBC] INSERT에 대한 자동 생성 키 값 검색하기 (0) | 2022.03.04 |
댓글