330 likes | 531 Views
고급자바프로그래밍 (Advanced Java Programming). 강원대학교 컴퓨터학부 2012 년 가을학기 담당교수 정충교. 3 장 템플릿. 변경이 거의 일어나지 않고 일정한 패턴을 유지하는 부분 응용에 따라 변경되는 부분. 예외처리를 제대로 하지 않으면 리소스 반환이 이루어지지 않아 서버 다운 위험 예외처림 예 리스트 3-2 리스트 3-3 반복적으로 나타나는 예외처리 문장들을 정리해야겠다. 3.2.2 변하는 부분과 변하지 않는 부분 분리.
E N D
고급자바프로그래밍(Advanced Java Programming) 강원대학교컴퓨터학부 2012년 가을학기 담당교수정충교
3장 템플릿 변경이 거의 일어나지 않고 일정한 패턴을 유지하는 부분 응용에 따라 변경되는 부분
예외처리를 제대로 하지 않으면 리소스 반환이 이루어지지 않아 서버 다운 위험 • 예외처림 예 • 리스트 3-2 • 리스트 3-3 • 반복적으로 나타나는 예외처리 문장들을 정리해야겠다.
3.2.2 변하는 부분과 변하지 않는 부분 분리 • 리스트 3-4 UserDao의 deleteAll메소드 • 리스트 3-6 변하는 부분만 별로의 메소드로추출 • 템플릿 메소드 패턴 적용 • 리스트 3-6의 UserDao의 makeStatement메소드를 추상 메소드로 설정 --> UserDao도 추상클래스가 됨 • 리스트 3-7 UserDao의 서브클래스
3.2.2 변하는 부분과 변하지 않는 부분 분리 템플릿 메소드 패턴을 적용한 결과 • 문제점 • Dao 메소드마다서브클래스를 만들어야 함 • 유연성 부족 - 컴파일 시점에 이미 클래스간 관계가 고정됨
3.2.2 변하는 부분과 변하지 않는 부분 분리 전략패턴의 적용 컨텍스트에서 하는 일 - 변하지 않는 일
3.2.2 변하는 부분과 변하지 않는 부분 분리 • StatementStategy전략 인터페이스 • public interface StatementStrategy { • PreparedStatementmakePreparedStatement(Connection c) throws SQLException; • } • 리스트 3-9 deleteAll기능의 StatementStrategy전략 클래스DeleteAllStatement • 리스트 3-10 deleteAll기능의 컨텍스트 • 문제점 - 컨텍스트 내에 구체적인 전략클래스가 고정되어 있음
DI 적용을 위한 클라이언트/컨텍스트 분리 그림 3-3 클라이언트가 있는 전략패턴 리스트 3-11 컨텍스트 코드 (jdbcContextWithStatementStrategy) 리스트 3-12 클라이언트 역할의 deleteAll코드
3.3.1 add 기능에 대해서도 동일한 패턴 적용 리스트 3-14 add 기능의 StatementStrategy전략 클래스 AddStatement 리스트 3-15 클라이언트 역할의 add 코드
3.3.2 전략과 클라이언트의 동거 • DeleteAllStatement, AddStatement클래스들을 로컬클래스 혹은 익명클래스로변환하여 클라이언트 내부에 삽입 • 간결하다. • 리스트 3-16 • 리스트 3-17 • 리스트 3-18 • 리스트 3-19 • 리스트 3-20 • --> spring30-3.3
3.4 컨텍스트와DI • 3.4.1 jdbcContext의 분리 • jdbcContextWithStatementStrategy메소드를 별도의 클래스로 분리 • --> 다른 Dao에서도 사용 가능 • 리스트 3-21 별도로 분리된 JdbcContext클래스 • 리스트 3-22 클라이언트 기능만 남은 UserDao클래스 • 리스트 3-23 설정파일 • --> spring30-3.4 • UserDao와 JdbcContext는 서로 연관이 깊어 JdbcContext가 다른 클래스로 대체될 가능성이 거의 없으므로 인터페이스를 통해 연결하지 않고 직접 연결함 • spring30-3.4은 아래 두 방법 중 B 방법을 적용함 • JdbcContext를 스프링 빈으로 등록하여 UserDao에 DI하는 방법 • JdbcContext를 스프링 빈으로 등록하지 않고 UserDao의 파라미터setter 메소드 코드가 직접 JdbcContext를 UserDao에 DI하는 방법 (리스트 3-24, 리스트 3-25)
3.5 템플릿과 콜백 • 3.5.1 템플릿/콜백의동작원리 • 프로젝트 spring30-3.4의 기본 구조 • 전략패턴 활용 • 클라이언트는 복잡하지만 일정 패턴을 갖는 작업 • 전략 인터페이스를 구현한 클래스들이 익명클래스 형태로 클라이언트에게 편입됨
그림 3-8 UserDao, JdbcContext, StatementStrategy에 적용된 템플릿/콜백 패턴 프로젝트 spring30-3.4 코드 참고
3.6 스프링의 JdbcTemplate • 3.5에서 우리가 직접 만들어 사용한 템플릿 • public class JdbcContext • 스프링은 JdbcContext와 유사한 그러나 더욱 강력한 클래스를 제공함 - JdbcTemplate
3.6 스프링의 JdbcTemplate • JdbcTemplate은 update 메소드를 지원함 • (http://static.springsource.org/spring/docs/3.0.x/javadoc-api/) • int update(PreparedStatementCreatorpsc) - A • int update(String sql) - B • int update(String sql, Object... args) - C
3.6 스프링의 JdbcTemplate • int update(PreparedStatementCreatorpsc) - A • int update(String sql) - B • int update(String sql, Object... args) - C 리스트 3-46 PreparedStatementCreator콜백을 인자로 주면서 update 호출 - A // UserDao의 메소드 public void deleteAll( ) { this.jdbcTemplate.update( new PreparedStatementCreator() { public PreparedStatementcreatePreparedStatement(Connection con) throws SQLException { return con.prepareStatement("delete from users"); } } ); }
3.6 스프링의 JdbcTemplate • int update(PreparedStatementCreatorpsc) - A • int update(String sql) - B • int update(String sql, Object... args) - C • 리스트 3-47 sql스트링을 인자로 주면서 update 호출 - B • JdbcTemplate이 스스로 (sql스트링을 참고하여) 콜백을 만들어 자기 자신에게 등록함 • // UserDao의 메소드 • public void deleteAll( ) { • this.jdbcTemplate.update("delete from users"); • }
3.6.2 getCount메소드에JdbcTemplate적용하기 • add, deleteAll의 경우 데이터베이스에 쓰기만 하면 되지만 getCount는 데이터베이스 질의 결과를 정수로 받아와야함 • JdbcTemplate은 query 메소드를지원함 • (http://static.springsource.org/spring/docs/3.0.x/javadoc-api/) • <T> T query(PreparedStatementCreatorpsc, ResultSetExtractor<T> rse) • JdbcTemplate오브젝트에게 query 메소드를 호출할 때는 두 개의 인자를 준다. • 하나는 PreparedStatementCreator, 다른 하나는 ResultSetExtractor이다. • 이 두 인자들이 바로 콜백오브젝트들이다.
3.6.2 getCount메소드에JdbcTemplate적용하기 • <T> T query(PreparedStatementCreatorpsc, ResultSetExtractor<T> rse) • JdbcTemplate의 query 메소드가 실행될 때 우선 PreparedStatementCreator의 createPreparedStatement메소드가 호출(콜백)되고 이 호출 결과로 반환되는 PreparedStatement가 execute된다. PreparedStatement를 execute하면 그 결과로 ResultSet이 반환된다. • query 메소드가 수행하는 두번째 작업은 ResultSetExtractor에게 extractData메소드를 호출(콜백)하는 것이다. 이 메소드를 호출할 때 질의 결과로 얻은 ResultSet을 인자로 준다. 이 extractData메소드가 반환하는 결과가 최종적으로 query 메소드의반환값이 된다.
3.6.2 getCount메소드에JdbcTemplate적용하기 • <T> T query(PreparedStatementCreatorpsc, ResultSetExtractor<T> rse) • <T>는 타입 파라미터이다. T 위치에 타입 이름이 들어갈 수 있다. T자리에 int가 들어가면 query 메소드는 아래와 같은 형식으로 쓰이게 된다. • Integer query(PreparedStatementCreatorpsc, ResultSetExtractor<Integer> rse) • 즉, Integer를 반환하는 ResultSetExtractor를 사용하면 최종 query 반환 타입도 Integer가 된다.
3.6.2 getCount메소드에JdbcTemplate적용하기 • <T> T query(PreparedStatementCreatorpsc, ResultSetExtractor<T> rse) • PreparedStatementCreator의 createPreparedStatement메소드와ResultSetExtractor의 extractData메소드가 각각 어떤 작용을 하게 할지는 익명클래스로 구현한다. • 리스트 3-49 JdbcTemplate을 이용해 만든 getCount()
3.6.2 getCount메소드에JdbcTemplate적용하기 • org.springframework.jdbc.core • Interface PreparedStatementCreator • PreparedStatementcreatePreparedStatement(Connection con) • Create a statement in this connection. • org.springframework.jdbc.core • Interface ResultSetExtractor<T> • T extractData(ResultSetrs) • Implementations must implement this method to process the entire ResultSet.
JdbcTemplate는 아래 메소드를 제공한다. • intqueryForInt(String sql) • Execute a query that results in an int value, given static SQL. • 스스로 두 개의 콜백을 만들어 사용하고 최종 결과를 반환 • 리스트 3-50 • public getCount() { • return this.jdbcTemplate.queryForInt("select count(*) from users");
3.6.2 get 메소드에JdbcTemplate적용하기 • add, deleteAll의 경우 데이터베이스에 쓰기만 하면 되고, getCount는 데이터베이스 질의 결과를 정수로 받아오면 되지만, get 메소드는User 객체를 반환해야 함 • JdbcTemplate은 queryForObject메소드를 지원함 • (http://static.springsource.org/spring/docs/3.0.x/javadoc-api/) • <T> T queryForObject(String sql, Object[] args, RowMapper<T> rowMapper) • Query given SQL to create a prepared statement from SQL and a list of arguments to bind to the query, mapping a single result row to a Java object via a RowMapper. • org.springframework.jdbc.core • Interface RowMapper<T> • T mapRow(ResultSetrs, introwNum) • Implementations must implement this method to map each row of data in the ResultSet.
리스트 3-51 queryForObject와 RowMapper를 적용한 get 메소드 public User get(String id) { return this.jdbcTemplate.queryForObject("select * from users where id = ?", new Object[] {id}, new RowMapper<User>() { public User mapRow(ResultSetrs, introwNum) throws SQLException { User user = new User(); user.setId(rs.getString("id")); user.setName(rs.getString("name")); user.setPassword(rs.getString("password")); return user; } }); }
3.6.4 테이블에 있는 레코드를 모두 다 읽어오는getAll메소드를UserDao에 추가 • UserDao의 getAll메소드는List<User>를 반환하도록 하면 좋을 것임 • 테스트 코드를 먼저 만든다. • UserDaoTest에 getAll테스트 메소드를 구현 -- 리스트 3-52 • UserDao의 getAll메소드 구현 -- 리스트 3-53 • <T> List<T> • query(String sql, RowMapper<T> rowMapper) • Execute a query given static SQL, mapping each row to a Java object via a RowMapper.
테스트 보완 • 네거티브 테스트! • 프로젝트 spring30-3.6.4 • 데이터베이스 드라이버 사용, 예외처리, 안전한 리소스 반환 등은 템플릿이 처리함 • UserDao에는 User 정보를 저장하거나 가져오는 로직만 남음 • 자유로운 DataSource선택 (DI를 통해) - xml 구성설정 파일을 통해 UserDao빈에 주입
3.6.5 재사용 가능한 콜백의분리 • UserDao에서는 더 이상 DataSource를 사용하지 않으므로 UserDao의 dataSource프로퍼티를없앰 • 중복제거 • 리스트 3-56 재사용 가능하도록 독립시킨 RowMapper • 리스트 3-57 공유 userMapper를 이용하도록 수정한 get, getAll메소드
UerDao JdbcTemplate getCount { } guery { ........ rs = ps.execute(); } query( , ) 호출 (callback) PreparedStatementCreator 생성 PreparedStatement반환 createPreparedStatement() { } 호출(callback) 생성 ResultSetExtractor 결과값 반환 extractData(rs) { } 결과값 반환 클라이언트 템플릿 콜백
역할의 분리 • UserDao • SQL • JdbcTemplate • JDBC API 사용 • 예외처리 • 리소스 반납 • DB 연결 획득
추가적 개선 • UserMapper를 스프링빈으로 • XML 설정 파일의 UserMapper프로퍼티로 아래 정보 기입 • User 테이블의 필드 – User 프로퍼티매핑 • --> User 테이블이 바뀔 때 xml 파일만 수정하면 됨 • UserDao에 나타나는 SQL 문장을 별도의 파일로 저장하고 이를 불러 사용 • --> User 테이블이 바뀔 때 별도의 파일 수정하면 됨