스프링처럼 jdbc 리팩터링하기
TRANSCRIPT
한국 스프링 사용자 모임 (http://ksug.org) 1
Refactoring JDBC programming
2008.10.12박찬욱
한국 스프링 사용자 모임 (http://ksug.org) 2
1. Hand-made JdbcTemplate1.1 문제 인식 공유
1.2 해결 방안 모색
1.3 Refactoring with Strategy pattern
2. Spring JdbcTemplate2.1 Best Practice of JDBC Strategy
2.2 Simplify JDBC operation
Table of Contents
한국 스프링 사용자 모임 (http://ksug.org) 3
오늘의 목표는 ?•Composition + interface-based 프로그래밍 기법
Spring JdbcTemplate 이해하기
어디 쓸 만한 곳이 없을까 ?
4 한국 스프링 사용자 모임 (http://ksug.org)
1. Hand-made JdbcTemplate
한국 스프링 사용자 모임 (http://ksug.org) 5
1.1 문제 인식 공유• 퀴즈
– iBatis– ORM(Hibernate or TopLink 등 )– 내부 추상화 프레임웍– 쌩 (pure) JDBC~?
한국 스프링 사용자 모임 (http://ksug.org) 6
1.1 문제 인식 공유• JDBC 근본적인 문제
– TCFTC• Try-Catch-Finally-Try-Catch• 최고의 boilerplate 코드
– Connection / Resource 누수 문제– SQLException
• 데이터베이스 벤더 별로 정의된 error code, error state 정보
Connection con = null; Statement stmt = null; try { con = dataSource.getConnection(); stmt = con.createStatement(); stmt.executeUpdate(“UPDATE TABLE_NAME SET...”); }catch(SQLException e){ // 예외 처리 ...}finally { if (stmt != null) { try { stmt.close(); } catch (SQLException ex) {} } if (con != null) try { con.close(); } catch (SQLException ex) { } }
한국 스프링 사용자 모임 (http://ksug.org) 7
1.1 문제 인식 공유• 전혀 OO 스럽지 않은 반복적인 코드 (boilerplate code) 의
사용으로 Data Access 코드가 드러워져 간다
“Bad Java code is bad J2EE code.”- Rod Johnson
한국 스프링 사용자 모임 (http://ksug.org) 8
1.2 해결 방안 모색
Strategy pattern 을 도입해서 문제 해결 시도
한국 스프링 사용자 모임 (http://ksug.org) 9
1.2 해결 방안 모색• Strategy pattern 이란 ?
– 실행 시점에 (at runtime) 알고리즘을 선택할 수 있는 방법
한국 스프링 사용자 모임 (http://ksug.org) 10
1.2 해결 방안 모색• 왜 Strategy pattern 을 택했나 ?
- Programming to Interface instead of Concrete Class (Achieving Loose Coupling with Interface)
- Favor object composition over class inheritance (Prefer Object Composition to Concrete Inheritance)
한국 스프링 사용자 모임 (http://ksug.org) 11
2.1 Best Practice of JDBC Strategy• Strategy pattern
참조 : wiki[2]
한국 스프링 사용자 모임 (http://ksug.org) 12
1.3 Refactoring with Strategy pattern• 변경되는 부분과 그렇지 않은 부분 식별하기 , 각각
– Updating sql 실행try {
// get DB connectioncon = getConnection();
// create Statementstmt = con.prepareStatement( "update LECTURE SET name=? where ID=?“);
// binding sql parameterstmt.setString(1, name);//...
// execute Queryresult = stmt.executeUpdate();
} catch (SQLException exception) {// 예외처리
} finally {if (stmt != null) { try {stmt.close();}catch(SQLException e){}}// end ifif (con != null) { try {con.close();}catch(SQLException e){}}// end if
}// end try-catch-finallyreturn result;
try {// get DB connectioncon = getConnection();
// create Statementstmt = con.prepareStatement( "insert into LECTURE values(?, ?, ?, ?, ?)“);
// binding sql parametersstmt.setInt(1, incrementer.nextIntValue());// ...
// execute Queryresult = stmt.executeUpdate();
} catch (SQLException exception) {// 예외처리
} finally {if (stmt != null) { try {stmt.close();}catch(SQLException e){}}// end ifif (con != null) { try {con.close();}catch(SQLException e){}}// end if
}// end try-catch-finallyreturn result;
update() insert()
한국 스프링 사용자 모임 (http://ksug.org) 13
1.3 Refactoring with Strategy pattern
try {// get DB connectioncon = getConnection();
// create Statementstmt = con.prepareStatement( "update LECTURE SET name=? where ID=?“);
// binding sql parameterstmt.setString(1, name);//...
// execute Queryresult = stmt.executeUpdate();
} catch (SQLException exception) {// 예외처리
} finally {if (stmt != null) { try {stmt.close();}catch(SQLException e){}}// end ifif (con != null) { try {con.close();}catch(SQLException e){}}// end if
}// end try-catch-finallyreturn result;
메소드 인자로 빼냄
콜백 메소드로 구현
공통 메소드로 추출
update()insert()
한국 스프링 사용자 모임 (http://ksug.org) 14
1.3 Refactoring with Strategy pattern
try {// get DB connectioncon = getConnection();
// create Statementstmt = con.prepareStatement( "update LECTURE SET name=? where ID=?“);
// binding sql parameterstmt.setString(1, name);//...
// execute Queryresult = stmt.executeUpdate();
} catch (SQLException exception) {// 예외처리
} finally {if (stmt != null) { try {stmt.close();}catch(SQLException e){}}// end ifif (con != null) { try {con.close();}catch(SQLException e){}}// end if
}// end try-catch-finallyreturn result;
update()insert()
한국 스프링 사용자 모임 (http://ksug.org) 15
1.3 Refactoring with Strategy pattern
try {// get DB connectioncon = getConnection();
// create Statementstmt = con.prepareStatement( "update LECTURE SET name=? where ID=?“);
// binding sql parameterstmt.setString(1, name);//...
// execute Queryresult = stmt.executeUpdate();
} catch (SQLException exception) {// 예외처리
} finally {if (stmt != null) { try {stmt.close();}catch(SQLException e){}}// end ifif (con != null) { try {con.close();}catch(SQLException e){}}// end if
}// end try-catch-finallyreturn result;
update()
한국 스프링 사용자 모임 (http://ksug.org) 16
1.3 Refactoring with Strategy pattern
try {// get DB connectioncon = getConnection();
// create Statementstmt = con.prepareStatement( " insert into LECTURE values(?, ?, ?, ?, ?)“);
// binding sql parameterstmt.setString(1, name);//...
// execute Queryresult = stmt.executeUpdate();
} catch (SQLException exception) {// 예외처리
} finally {if (stmt != null) { try {stmt.close();}catch(SQLException e){}}// end ifif (con != null) { try {con.close();}catch(SQLException e){}}// end if
}// end try-catch-finallyreturn result;
insert()
한국 스프링 사용자 모임 (http://ksug.org) 17
1.3 Refactoring with Strategy pattern• Strategy pattern 이 적용된 Template.update()
한국 스프링 사용자 모임 (http://ksug.org) 18
1.3 Refactoring with Strategy pattern• 변경되는 부분과 그렇지 않은 부분 식별하기 , 각각
– querying sql 실행try {
// get DB connectioncon = getConnection();
// create Statementrs = con.prepareStatement( “select * from LECTURE where ID=?“);
// binding sql parameterstmt.setInt(1, id);
// execute Queryrs = stmt.executeQuery();
//extract resultwhile (rs.next()) {
result = new Lecture();Result.setName(rs.getString(2));// extracting...
}
} catch (SQLException exception) {// 예외처리
} finally {// 자원 반환 처리
}return result;
try {// get DB connectioncon = getConnection();
// create Statementrs = con.prepareStatement( “select * from LECTURE“);
// binding sql parameter
// execute Queryrs = stmt.executeQuery();
//extract resultwhile (rs.next()) {
lecture = new Lecture();Result.setName(rs.getString(2));// extracting...
result.add(lecture);}
} catch (SQLException exception) {// 예외처리
} finally {// 자원 반환 처리
}return result;
get() getall()
한국 스프링 사용자 모임 (http://ksug.org) 19
1.3 Refactoring with Strategy pattern
try {// get DB connectioncon = getConnection();
// create Statementrs = con.prepareStatement( “select * from LECTURE where ID=?“);
// binding sql parameterstmt.setInt(1, id);
// execute Queryrs = stmt.executeQuery();
//extract resultwhile (rs.next()) {
result = new Lecture();Result.setName(rs.getString(2));// extracting...
}
} catch (SQLException exception) {// 예외처리
} finally {// 자원 반환 처리
}return result;
get()
메소드 인자로 빼냄
콜백 메소드로 구현
공통 메소드로 추출
getall()
한국 스프링 사용자 모임 (http://ksug.org) 20
1.3 Refactoring with Strategy pattern
try {// get DB connectioncon = getConnection();
// create Statementrs = con.prepareStatement( “select * from LECTURE where ID=?“);
// binding sql parameterstmt.setInt(1, id);
// execute Queryrs = stmt.executeQuery();
//extract resultwhile (rs.next()) {
result = new Lecture();Result.setName(rs.getString(2));// extracting...
}
} catch (SQLException exception) {// 예외처리
} finally {// 자원 반환 처리
}return result;
get()getall()
한국 스프링 사용자 모임 (http://ksug.org) 21
1.3 Refactoring with Strategy pattern
try {// get DB connectioncon = getConnection();
// create Statementrs = con.prepareStatement( “select * from LECTURE where ID=?“);
// binding sql parameterstmt.setInt(1, id);
// execute Queryrs = stmt.executeQuery();
//extract resultwhile (rs.next()) {
result = new Lecture();Result.setName(rs.getString(2));// extracting...
}
} catch (SQLException exception) {// 예외처리
} finally {// 자원 반환 처리
}return result;
getall()
한국 스프링 사용자 모임 (http://ksug.org) 22
1.3 Refactoring with Strategy pattern
try {// get DB connectioncon = getConnection();
// create Statementrs = con.prepareStatement( “select * from LECTURE where ID=?“);
// binding sql parameterstmt.setInt(1, id);
// execute Queryrs = stmt.executeQuery();
//extract resultwhile (rs.next()) {
result = new Lecture();Result.setName(rs.getString(2));// extracting...
}
} catch (SQLException exception) {// 예외처리
} finally {// 자원 반환 처리
}return result;
get()
한국 스프링 사용자 모임 (http://ksug.org) 23
1.3 Refactoring with Strategy pattern• Strategy pattern 이 적용된 Template.query()
한국 스프링 사용자 모임 (http://ksug.org) 24
1.3 Refactoring with Strategy pattern• Refactoring 1.
– 결과 값의 타입은 ?– ResultSet 처리
• 동일한 DTO 인 경우 , 거의 동일하게 ResultSet 에서 값을 빼내는 코드가 반복됨
return (Lecture) template.query("select * from LECTURE where ID=?",...}, new ResultSetExtractor() {
public Object extractResult(ResultSet rs)throws SQLException {
// extract result to Single ObjectLecture result = new Lecture();
while (rs.next()) {result.setId(rs.getInt(1));result.setName(rs.getString(2));result.setSpeaker(rs.getString(3));
}
return result;
}});
return (List<Lecture>) template.query("select * from LECTURE",...}, new ResultSetExtractor() {
public Object extractResult(ResultSet rs)throws SQLException {
// extract result to Collection ObjectList<Lecture> result =
new ArrayList<Lecture>();
while (rs.next()) {Lecture lecture = new Lecture();lecture.setId(rs.getInt(1));lecture.setName(rs.getString(2));lecture.setSpeaker(rs.getString(3));
result.add(lecture);}return result;
}});
get() getall()
한국 스프링 사용자 모임 (http://ksug.org) 25
1.3 Refactoring with Strategy pattern
return (Lecture) template.query("select * from LECTURE where ID=?",...}, new ResultSetExtractor() {
public Object extractResult(ResultSet rs)throws SQLException {
// extract result to Single ObjectLecture result = new Lecture();
while (rs.next()) {result.setId(rs.getInt(1));result.setName(rs.getString(2));result.setSpeaker(rs.getString(3));
}
return result;
}});
get()
콜백 메소드로 구현
API 로 구분
한국 스프링 사용자 모임 (http://ksug.org) 26
1.3 Refactoring with Strategy pattern
return (Lecture) template.query("select * from LECTURE where ID=?",...}, new ResultSetExtractor() {
public Object extractResult(ResultSet rs)throws SQLException {
// extract result to Single ObjectLecture result = new Lecture();
while (rs.next()) {result.setId(rs.getInt(1));result.setName(rs.getString(2));result.setSpeaker(rs.getString(3));
}
return result;
}});
get()
한국 스프링 사용자 모임 (http://ksug.org) 27
1.3 Refactoring with Strategy pattern
return (Lecture) template.query("select * from LECTURE where ID=?",...}, new ResultSetExtractor() {
public Object extractResult(ResultSet rs)throws SQLException {
// extract result to Single ObjectLecture result = new Lecture();
while (rs.next()) {result.setId(rs.getInt(1));result.setName(rs.getString(2));result.setSpeaker(rs.getString(3));
}
return result;
}});
get()
한국 스프링 사용자 모임 (http://ksug.org) 28
1.3 Refactoring with Strategy pattern• Refactoring 2.
– 여전히 Template 의 query() 와 update() 에서 JDBC API가 중복되어 사용된다 !
한국 스프링 사용자 모임 (http://ksug.org) 29
1.3 Refactoring with Strategy pattern• Refactoring 2. 결과
한국 스프링 사용자 모임 (http://ksug.org) 30
1.3 Refactoring with Strategy pattern• 콜백 인터페이스가 적용된 공통 메소드
한국 스프링 사용자 모임 (http://ksug.org) 31
1.4 Summary• 구현한 템플릿 클래스와 인터페이스
한국 스프링 사용자 모임 (http://ksug.org) 32
1.4 Summary• JdbcTemplate 도입된 이후의 효과
– JDBC workflow 의 흐름 진행 주체• DAO JdbcTemplate
– DAO 의 역할 충실화• SQL, 파라미터 제공 or 결과 매핑• 이 외의 다른 역할은 JdbcTemplate 를 비롯한 각 담당자에게 위임
한국 스프링 사용자 모임 (http://ksug.org) 33
2. SPRING JDBCTEMPLATE
한국 스프링 사용자 모임 (http://ksug.org) 34
2.1 Best Practice of JDBC Strategy• Spring JDBC core package’s Central class• Jdbc UseCase Best Practice• Collaborate with Various Callback Interface
Strategy pattern!• Convenience DA operation
"This is a special case of the Strategy design pattern. It appears different because the interface involved are so simple"
한국 스프링 사용자 모임 (http://ksug.org) 35
2.1 Best Practice of JDBC Strategy• JdbcTemplate with Strategy pattern
DAO
Template
CallbackInterface
implementation참조 : wiki[2]
한국 스프링 사용자 모임 (http://ksug.org) 36
2.1 Best Practice of JDBC Strategy
Jdbc-based DAO Spring-based DAODriverManager / DataSource
DataSource
Statement / PreparedStatement / CallableStatement
JdbcTemplate / Callback interface
ResultSet POJO / POJO’s collection
한국 스프링 사용자 모임 (http://ksug.org) 37
2.1 Best Practice of JDBC Strategy
Task Spring You
Connection(DataSource) management
Provide SQL
Statement management
Parameter Declaration
Provide parameter value
ResultSet management
Row Data Retrieval
Transaction management
Exception handling
한국 스프링 사용자 모임 (http://ksug.org) 38
2.1 Best Practice of JDBC Strategy• Convenience, but powerful Jdbc Template
– Resource management• DataSourceUtils
– Integrated with Transaction management• Spring-tx (non-invasive)
한국 스프링 사용자 모임 (http://ksug.org) 39
2.1 Best Practice of JDBC Strategy• Convenience, but powerful JdbcTemplate
– Consistent exception management• 예외 발생 시 처리 기준
– 에러 코드 (error code), 에러 상태 (error state) 예외 종류 (type of Exception)
• Check exception Unchecked exception• SQLExceptionTranslator
한국 스프링 사용자 모임 (http://ksug.org) 40
2.1 Best Practice of JDBC Strategy• Convenience, but powerful JdbcTemplate
– Logging for SQL inform. (DEBUG level)– Various Template
• JdbcTemplate• NamedParameterJdbcTemplate• SimpleJdbcTemplate
– Convenience DA operation• named parameter• Auto-detect column by Jdbc Driver• Easily using Batch, LOB
한국 스프링 사용자 모임 (http://ksug.org) 41
2.1 Best Practice of JDBC Strategy• JdbcTemplate 구성 방법
– DataSource(or Connection Pool) 가 쓰레드 안전하다면 , JdbcTemplate 도 쓰레드 안전
private JdbcTemplate template;
public void setTemplate(DataSource dataSource) {this.template = new JdbcTemplate(dataSource);
}
<bean id="lectureDao" class="org.springframework.lecture.jdbc.dao.JdbcLectureDao">
<property name="template" ref="dataSource" /></bean>
private JdbcTemplate template;
public void setTemplate(JdbcTemplate template) {this.template = template;
}
<bean id="lectureDao" class="org.springframework.lecture.jdbc.dao.JdbcLectureDao">
<property name="template" ref="jdbcTemplate" /></bean>
한국 스프링 사용자 모임 (http://ksug.org) 42
2.2 SIMPLIFY JDBC OPERATION
한국 스프링 사용자 모임 (http://ksug.org) 43
2.2 Simplify JDBC operation• SimpleJdbc* 활용하기
– SimpleJdbcTemplate• Wrapper around classic JdbcTemplate
class(getJdbcOperations())• Java-5-based convenience wrapper for the classic Spring
JdbcTemplate• Varargs, Generic, Autoboxing, Unboxing...
한국 스프링 사용자 모임 (http://ksug.org) 44
2.2 Simplify JDBC operation• SimpleJdbc* 활용하기
– SimpleJdbcInsert• Simplify Insert behavior• JdbcTemplate + DatabaseMetaData• ‘fluid’ interface style
한국 스프링 사용자 모임 (http://ksug.org) 45
2.2 Simplify JDBC operation• SimpleJdbc* 활용하기
– SimpleJdbcCall• multi-threaded, reusable object representing a call to stored
procedure or a stored function
– SimpleJdbcTestUtils
한국 스프링 사용자 모임 (http://ksug.org) 46
2.2 Simplify JDBC operation• The Pareto Principle in action
– JdbcTemplate+callback interface by Reflection = 80– SqlQuery + inheritance by explicit parameter
mapping =20
한국 스프링 사용자 모임 (http://ksug.org) 47
2.2 Simplify JDBC operation• RowMapper(with ResultSetExtractor)
– per-row basis– Stateless & reusable– Ideal choice of row-mapping logic
• ResultSetExtractor– per-resultSet basis– Stateless & reusable, if not access stateful resource
한국 스프링 사용자 모임 (http://ksug.org) 48
2.2 Simplify JDBC operation• SqlQuery
– by Inheritance– Reusable, threadsafe class– Encapsulate SQL– MappingSqlQuery & UpdatableSqlQuery– Using meaningful method name
한국 스프링 사용자 모임 (http://ksug.org) 49
2.2 Simplify JDBC operation• RowCallbackHandler
– Stateful– public void processRow(ResultSet rs) throws
SQLException
한국 스프링 사용자 모임 (http://ksug.org) 50
2.2 Simplify JDBC operation• DataFieldMaxValueIncrementer 활용하기
– Sequence-based• Oracle, PostgreSQL, DB2(plain, mainframe), HSQL, H2
– Column-based• MySQL, MS-SqlServer, Sybase, Hsql, Derby
• BeanProperty* 활용하기– (Parameterized)BeanPropertyRowMapper– BeanPropertySqlParameterSource
한국 스프링 사용자 모임 (http://ksug.org) 51
Reference• wiki[1]:
http://en.wikipedia.org/wiki/Image:Strategy_Pattern_Diagram_ZP.svg
• wiki[2]: http://en.wikipedia.org/wiki/Image:Strategy_pattern_in_LePUS3.gif
• Tomas[1]: JDBC Development with the Spring Framework
• Spring reference• Spring API• J2EE Design and Development• J2EE without EJB
한국 스프링 사용자 모임 (http://ksug.org) 52
감사합니다 .