이전글:
- 2013/10/22 - [개발자이야기] - 작은 프로젝트 구현 이야기 2 - DB 코드 백업 툴 설계 변경
- 2013/10/21 - [개발자이야기] - 작은 프로젝트 구현 이야기 1 - DB 코드(plsql) 백업툴 초기 설계
드디어 DB에서 변경된 DB 코드(프로시저, 함수, 패키지) 목록을 뽑아주는 쿼리를 전달받았다. 애초에 상상한 쿼리 실행 결과는 아래와 같았다.
[상상속에서 기대했던 쿼리 실행 결과]
OWNER |
TYPE |
NAME |
QUERY |
O1 |
PROCEDURE |
findXXX |
프로시저 코드 |
O2 |
PACKAGE |
somePkg |
패키지 코드 |
... | ... | ... | ... |
그런데 이게 왠 걸? 실제 쿼리를 실행하지 아래와 같은 결과가 나왔다.
[실제 쿼리 실행 결과]
OWNER | TYPE | NAME | QUERY |
O1 | PROCEDURE | findXXX | 프로시저 코드 1번줄 |
O1 | PROCEDURE | findXXX | 프로시저 코드 2번줄 |
... | ... | ... | ... |
O1 | PROCEDURE | findXXX | 프로시저 코드 n번줄 |
O2 | PACKAGE | somePkg | 패키지 코드 1번줄 |
쿼리를 갖고 몇 가지 방법을 쪼물딱 거려 봤으나, 줄 수가 좀(많이) 긴 패키지의 경우 오라클 장비가 먹어주지 못하는 상황이 발생했다. 이런 짜증 날 때를 봤나. 게다가 몇 만 줄이나 되는 패키지가 있다. (실제로 2만 줄이 넘는 패키지가 있다.)
한 번에 모든 쿼리 데이터를 다 읽어올까? 아님 필요할 때 읽어올까? 잠시 고민을 해 봤는데, 최초에 모든 쿼리를 받아서 SVN 리포지토리에 업로드해 주어야 하는 상황이 필요했고. (실DB에 수만 줄에 해당하는 프로시저 덩어리가 얼마나 있는 지 몰라 한방에 모든 DB 코드를 다 읽어오지 않고) DB 코드가 필요한 순간에 코드를 읽어오도록 DbCode 클래스 및 DbCodeFinder 의 코드를 수정했다. 먼저 다음과 같이 getDdl() 메서드를 호출하는 순간에 실제 쿼리 내용을 읽어오는 LazyDbCode 클래스를 추가했다.
public class LazyDbCode extends DbCode {
private JdbcTemplate jdbcTemplate;
public LazyDbCode(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public String getDdl() {
String query = "SELECT B.TEXT FROM ALL_OBJECTS A, ALL_SOURCE B "
+ "WHERE A.OWNER = ? AND A.OBJECT_NAME = ? "
+ "AND A.OBJECT_TYPE = ? AND A.OBJECT_NAME = B.NAME "
+ "AND A.OBJECT_TYPE = B.TYPE ";
List<String> queryLines = jdbcTemplate.query(query,
new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
ps.setString(1, getSchema());
ps.setString(2, getName());
ps.setString(3, getType().name());
}
}, new RowMapper<String>() {
public String mapRow(ResultSet rs, int rowNum) throws SQLException {
return rs.getString(1);
}
});
return queryLinesToString(queryLines);
}
public String queryLinesToString(List<String> queryLines) {
...
}
}
DbCodeFinder의 구현 클래스인 JdbcDbCodeFinder는 DbCode 대신에 LazyDbCode 객체를 생성해서 리턴하도록 구현했다.
public class JdbcDbCodeFinder implements DbCodeFinder {
private JdbcTemplate jdbcTemplate;
public List<DbCode> findUpdatedDbCodesAfter(final Date fromTime, final Date toTime) {
String query = "SELECT * FROM ALL_OBJECTS A "
+ "WHERE SUBSTR(A.OWNER,1,1) IN ('A','B','C','D','E','H') "
+ "AND A.OBJECT_TYPE IN ('PACKAGE', 'PACKAGE BODY', 'FUNCTION', 'PROCEDURE', 'TRIGGER') "
+ "AND TO_CHAR(A.LAST_DDL_TIME, 'YYYYMMDDHH24MISS') BETWEEN ? AND ? "
+ "ORDER BY A.OBJECT_NAME, A.OBJECT_TYPE";
List<DbCode> dbCodeList = jdbcTemplate.query(query,
new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
ps.setString(1, formatDate(fromTime));
ps.setString(2, formatDate(toTime));
}
}, new RowMapper<DbCode>() {
public DbCode mapRow(ResultSet rs, int rowNum) throws SQLException {
DbCode code = new LazyDbCode(jdbcTemplate);
code.setSchema(rs.getString("OWNER"));
code.setType(DbCode.Type.valueOf(rs.getString("OBJECT_TYPE")));
code.setName(rs.getString("OBJECT_NAME"));
return code;
}
});
return dbCodeList;
}
SvnClient와 DbCodeFinder의 콘크리트 클래스를 구현이 완료된 시점의 설계는 아래와 같다.
[다음 이야기에서 계속 ...]