/////
Search

221024

작성자
김지영
김희정
이현주
날짜
2022/10/24
학습 내용
토비의 스프링, 익명클래스, 템플릿콜백, JdbcTemplate
텍스트

토비의 스프링/ 김지영

익명클래스 적용

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이란
final을 썼을 때 이점
1.
신뢰성 - 불변이기 때문에 변화를 고려하지 않아도 됨.
2.
Memory를 적게 쓴다. - 바뀔 여지가 없기 때문에 바뀌는데 필요한 메모리 할당이 필요 없음.
3.
DI하고 나서 DataSource가 바뀌는 경우 - 무슨일이 일어날지 예측이 안됨.
final을 쓰는 이유
1.
Spring에서 DI되었다면 이미 Factory에서 조립이 끝난 상태이므로 변화하지 않는게 좋음.
2.
메모리 사용에 유리하고 신뢰성 있기 때문
3.
이후 SpringBoot에서 @Autowired하는 부분이 final로 대체하는 것을 권장하게 바뀜.

Bean은 언제 사용할까?

Spring의 ApplicationContext에 등록하는 빈을 만들 때 ex) AwsUserDao 등 재료가 되는 @Bean 도 붙여주는 경우가 있다. 조립의 재료로만 쓴다면 ApplicationContext에 등록을 안해줘도 된다.

22.10. 24. 회고 - 이현주