강의5 - 강원대학교

Download Report

Transcript 강의5 - 강원대학교

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