2021 05 22
2021-05-22¶
토비의 Spring [3장. 템플릿]¶
- 핵심 내용
- [템플릿]
- "변경이 거의 일어나지 않으며 일정한 패턴으로 유지되는 특성"을 가진 부분을,
"자유롭게 변경되는 성질을 가진 부분"으로부터 독립시켜서 효과적으로 활용할 수 있도록 하는 방법- [JDBC] - 일반적으로 서버에서는 제한된 갯수의 DB 커넥션을 만들어서 재사용 가능한 풀로 관리 - 미처 반환되지 못한 Connection이 계속 쌓이면 리소스 모자라다는 오류 발생 - try/catch/finally 구문을 통해 리소스 반환
- [전략 패턴을 통한 JDBC 구현] - StatementStrategy 인터페이스를 작성
public interface StatementStrategy { PreparedStatement makePreparedStatement(Connection c) throws SQLException; }- 해당 인터페이스를 구현하여 구체적인 SQLStatement를 작성 - 하지만 전략 사용자가 직접 전략을 선택해야함 - 전략 패턴 정신에 위배 - 익명 클래스를 통한 리팩토링 적용- [스프링 DI] - DI의 근본... 인터페이스를 사이에 두고 클래스 레벨에서는 의존 관계 고정 X, 런타임 주입 O - 하지만 UserDao와 JdbcContext는 각각 클래스임에도 DI 구조를 가져도 괜찮다 1. Singleton으로써 Spring Container의 관리를 받을 수 있음 2. JdbcContext가 DI를 통해 여러 Bean에서 쓰일 수 있음 3. UserDao와 JdbcContext는 애초에 매우 긴밀한 관계로 태어남 - 강한 응집도를 가져서 어차피 바꿀라면 다 바꿔야함
- [템플릿/콜백 패턴] - "전략 패턴"과 "DI의 장점"을 "내부 클래스 사용 전략"과 결합한 사용법 - 템플릿: 전략 패턴의 컨텍스트 (전략 사용자) - 콜백: 내부 클래스로 만들어지는 오브젝트 - 매번 메서드 단위로 사용할 오브젝트를 새롭게 전달받음 - try/catch/finally 블록을 사용하는 코드는 템플릿/콜백 패턴의 후보 - 제네릭스 응용을 통해 리턴 값을 갖는 템플릿/콜백 패턴 구현 가능
- 스프링은 JDBC 코드 작성을 위해 JdbcTemplate을 기반으로 하는 다양한 템플릿과 콜백을 제공
- 계산기 템플릿/콜백 실습
//Callback Interface public interface LineCallback<T> { T doSomethingWithLine(String line, T value); } //Client public int calcSum(String path) throws IOException { //Callback LineCallback<Integer> sumCallback = (line, value) -> value + Integer.parseInt(line); return lineReadTemplate(path, sumCallback, 0); } //Template private <T> T lineReadTemplate(String path, LineCallback<T> callback, T initVal) throws IOException { BufferedReader br = null; try { br = new BufferedReader(new FileReader(path)); T res = initVal; String line = null; while ((line = br.readLine()) != null) { res = callback.doSomethingWithLine(line, res); } return res; } catch (IOException e) { throw e; } finally { if (br != null) { try { br.close(); } catch (IOException e) { throw e; } } } }- 실행 순서 1. calcSum에서 sumCallback이라는, LineCallback 인터페이스의 클래스 구현체를 생성 2. lineReadTemplate으로 sumCallback을 전달 3. lineReadTemplate에서는 자원 할당을 하며 필요한 작업 시작 4. sumCallback을 호출하여 익명 클래스에서 doSomethingWithLine에 대응하는 작업 수행 5. 모든 처리가 끝나면 Template 작업 결과 반환
- JdbcTemplate 살펴보기
//UserDao.deleteAll() public void deleteAll() { this.jdbcTemplate.update("delete from users"); } //StatementCallback Interface @FunctionalInterface public interface StatementCallback<T> { @Nullable T doInStatement(Statement stmt) throws SQLException, DataAccessException; } //JdbcTemplate.update() @Override public int update(final String sql) throws DataAccessException { Assert.notNull(sql, "SQL must not be null"); if (logger.isDebugEnabled()) { logger.debug("Executing SQL update [" + sql + "]"); } //StatementCallback의 클래스 구현체 class UpdateStatementCallback implements StatementCallback<Integer>, SqlProvider { @Override public Integer doInStatement(Statement stmt) throws SQLException { int rows = stmt.executeUpdate(sql); if (logger.isDebugEnabled()) { logger.debug("SQL update affected " + rows + " rows"); } return rows; } @Override public String getSql() { return sql; } } //Template으로 Callback 전송 return updateCount(execute(new UpdateStatementCallback())); } //JdbcTemplate.execute() @Override @Nullable public <T> T execute(StatementCallback<T> action) throws DataAccessException { Assert.notNull(action, "Callback object must not be null"); Connection con = DataSourceUtils.getConnection(obtainDataSource()); Statement stmt = null; try { stmt = con.createStatement(); applyStatementSettings(stmt); //Callback 수행 T result = action.doInStatement(stmt); handleWarnings(stmt); return result; } catch (SQLException ex) { String sql = getSql(action); JdbcUtils.closeStatement(stmt); stmt = null; DataSourceUtils.releaseConnection(con, getDataSource()); con = null; throw translateException("StatementCallback", sql, ex); } finally { JdbcUtils.closeStatement(stmt); DataSourceUtils.releaseConnection(con, getDataSource()); } }
JDBC¶
- 참고: https://kouzie.github.io/jdbc/JDBC.-1%EC%9D%BC%EC%B0%A8/#jdbc
- 정의 - Java Database Connectivity의 약자로 다양한 DB에 연결/작업을 위한 자바 표준 interface
- JDBC Interface - ORACLE, MongoDB, Mysql, MSSQL 등 각종 다른 DBMS에 접근시 각기 다른 종류의 메서드 필요 - JDBC 인터페이스로 모든 DB에 같은 메서드로 연동하여 쿼리 - 해당 JDBC 인터페이스를 구현한 클래스들을 DB 회사에서 jar 파일로 배포
- JDBC Driver Manager - DriverManager 클래스를 통해 DB 접속 - 프로그램 -> DriverManager -> JDBC Interface -> JDBC Driver -> DB - Connection 순서 1. Class.forName()으로 JDBC 드라이버 메모리에 로딩 2. Connection 객체 생성 3. 필요한 작업 4. 연결 종료