토비의 스프링/ 김지영
익명클래스 적용
public void jdbcContextWithStatementStrategy(StatementStrategy stmt) throws SQLException
Java
복사
public void deleteAll() throws SQLException {
// "delete from users"
jdbcContextWithStatementStrategy(new StatementStrategy() {
@Override
public PreparedStatement makeStatement(Connection conn) throws SQLException {
return conn.prepa reStatement("delete from users");
}
});
}
Java
복사
public void add(final User user) throws SQLException {
// DB접속 (ex sql workbeanch실행)
jdbcContextWithStatementStrategy(new StatementStrategy() {
@Override
public PreparedStatement makeStatement(Connection conn) throws SQLException {
PreparedStatement pstmt = null;
pstmt = conn.prepareStatement("INSERT INTO users(id, name, password) VALUES(?,?,?);");
pstmt.setString(1, user.getId());
pstmt.setString(2, user.getName());
pstmt.setString(3, user.getPassword());
return pstmt;
}
});
}
Java
복사
•
익명 내부 클래스를 도입한 이유: StatementStrategy Interface의 구현체인 DeleteAllStrategy()를 쓰는 곳이 deleteAll()한군데 뿐이기 때문에 굳이 class를 새로 만들 필요가 없다.
JdbcContext로 분리
public class JdbcContext {
}
Java
복사
public class JdbcContext {
private DataSource dataSource;
public JdbcContext(DataSource dataSource) {
this.dataSource = dataSource;
}
public void workWithStatementStrategy(StatementStrategy stmt) throws SQLException {
Connection c = null;
PreparedStatement pstmt = null;
try {
c = dataSource.getConnection();
pstmt = stmt.makeStatement(c);
// Query문 실행
pstmt.executeUpdate();
} catch (SQLException e) {
throw e;
} finally {
if (pstmt != null) {
try {
pstmt.close();
} catch (SQLException e) {
}
}
if (c != null) {
try {
c.close();
} catch (SQLException e) {
}
}
}
}
}
Java
복사
public class UserDao {
private final DataSource dataSource;
private final JdbcContext jdbcContext;
public UserDao(DataSource dataSource) {
this.dataSource = dataSource;
this.jdbcContext = new JdbcContext(dataSource);
}
public void add(final User user) throws SQLException {
// DB접속 (ex sql workbeanch실행)
jdbcContext.workWithStatementStrategy(new StatementStrategy() {
@Override
public PreparedStatement makeStatement(Connection conn) throws SQLException {
PreparedStatement pstmt = null;
pstmt = conn.prepareStatement("INSERT INTO users(id, name, password) VALUES(?,?,?);");
pstmt.setString(1, user.getId());
pstmt.setString(2, user.getName());
pstmt.setString(3, user.getPassword());
return pstmt;
}
});
}
Java
복사
jdbcContextWithStatementStrategy의 분리
•
분리하는 이유: jdbcContextWithStatementStrategy는 어디 하나에 종속되지 않고 다른 Dao에서도 사용이 가능하기 때문에 UserDao에서 분리한다.
Template Callback 적용
public void executeSql(String sql) throws SQLException {
this.jdbcContext.workWithStatementStrategy(new StatementStrategy() {
@Override
public PreparedStatement makePreparedStatement(Connection connection) throws SQLException {
return connection.prepareStatement(sql);
}
});
}
public void deleteAll() throws SQLException {
this.executeSql("delete from users");
// 쿼리만 넘기게 함
}
Java
복사
JdbcContext.java
public void executeSql(String sql) throws SQLException {
this.workWithStatementStrategy(new StatementStrategy() {
@Override
public PreparedStatement makePreparedStatement(Connection connection) throws SQLException {
return connection.prepareStatement(sql);
}
});
}
Java
복사
UserDao.java
public void deleteAll() throws SQLException {
this.jdbcContext.executeSql("delete from users");
}
Java
복사
•
TemplateCallback을 적용하는 이유: 앞에서 사용했던 익명 클래스는 중복되는 부분이 있고 UserDao외에도 사용할 가능성이 있다. 따라서 이렇게 재사용 가능한 콜백을 담고 있는 메소드는 Dao가 공유할 수 있는 템플릿 클래스 안으로 옮겨도 된다.
•
익명 클래스를 JdbcContext 클래스에 콜백 생성과 템플릿 호출이 담긴 executeSql()메소드를 생성해서 옮긴다.
JdbcTemplate 적용
UserDao.java
private JdbcTemplate jdbcTemplate;
public UserDao(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
Java
복사
deleteAll()
public void deleteAll() throws SQLException {
this.jdbcTemplate.update("delete from users");
}
Java
복사
add()
public void add(final User user) throws SQLException {
this.jdbcTemplate.update("insert into users(id, name, password) values (?, ?, ?);",
user.getId(), user.getName(), user.getPassword());
}
Java
복사
getCount()
public int getCount() throws SQLException {
return this.jdbcTemplate.queryForObject("select count(*) from users;", Integer.class);
}
Java
복사
findById()
public User findById(String id) {
String sql = "select * from users where id = ?";
RowMapper<User> rowMapper = new RowMapper<User>() {
@Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User(rs.getString("id"), rs.getString("name"),
rs.getString("password"));
return user;
}
};
return this.jdbcTemplate.queryForObject(sql, rowMapper, id);
}
Java
복사
getAll()
public List<User> getAll() {
String sql = "select * from users order by id";
RowMapper<User> rowMapper = new RowMapper<User>() {
@Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User(rs.getString("id"), rs.getString("name"),
rs.getString("password"));
return user;
}
};
return this.jdbcTemplate.query(sql, rowMapper);
}
Java
복사
•
스프링은 JDBC를 이용하는 Dao에서 사용할 수 있도록 준비된 다양한 템플릿과 콜백을 제공한다.
•
따라서 앞에서 만들었던 JdbcContext대신 스프링이 제공하는 JdbcTemplate을 사용한다.
RowMapper의 중복 제거
RowMapper<User> rowMapper = new RowMapper<User>() {
@Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User(rs.getString("id"), rs.getString("name"),
rs.getString("password"));
return user;
}
};
public User findById(String id) {
String sql = "select * from users where id = ?";
return this.jdbcTemplate.queryForObject(sql, rowMapper, id);
}
public List<User> getAll() {
String sql = "select * from users order by id";
return this.jdbcTemplate.query(sql, rowMapper);
}
Java
복사
RowMapper<User> rowMapper = new RowMapper<User>() {
@Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User(rs.getString("id"), rs.getString("name"),
rs.getString("password"));
return user;
}
};
public User findById(String id) {
String sql = "select * from users where id = ?";
return this.jdbcTemplate.queryForObject(sql, rowMapper, id);
}
public List<User> getAll() {
String sql = "select * from users order by id";
return this.jdbcTemplate.query(sql, rowMapper);
}
Java
복사
•
get() 메소드와 getAll() 메소드에 rowMapper가 중복되기도 하고 앞으로도 UserDao에 기능이 더 추가 될 것을 생각해 따로 메소드로 빼서 중복을 제거해 준다.
토비의 스프링 3장- 김희정
토비의 스프링 3장
질문
DI할 때 final을 쓰는 이유
final을 썼을 때 이점
1.
신뢰성 - 불변이기 때문에 변화를 고려하지 않아도 됨.
2.
Memory를 적게 쓴다. - 바뀔 여지가 없기 때문에 바뀌는데 필요한 메모리 할당이 필요 없음.
3.
DI하고 나서 DataSource가 바뀌는 경우 - 무슨일이 일어날지 예측이 안됨.
final을 쓰는 이유
1.
Spring에서 DI되었다면 이미 Factory에서 조립이 끝난 상태이므로 변화하지 않는게 좋음.
2.
메모리 사용에 유리하고 신뢰성 있기 때문
3.
이후 SpringBoot에서 @Autowired하는 부분이 final로 대체하는 것을 권장하게 바뀜.
Bean은 언제 사용할까?
•
Spring의 ApplicationContext에 등록하는 빈을 만들 때 ex) AwsUserDao 등 재료가 되는 @Bean 도 붙여주는 경우가 있다. 조립의 재료로만 쓴다면 ApplicationContext에 등록을 안해줘도 된다.