카테고리는 조직도와 같이 부모-자식 관계를 가지고 있으며 항목들을 일목요연하게 보여줄 수 있는 방법이다. 하지만 부모-자식 관계는 데이터베이스에 명확하게 표현할 수 있어도 프리젠테이션 영역에서 표현하기가 어렵다. 따라서 보통 카테고리를 표시하기 위해 '01/01/01' 카테고리의 순서와 깊이를 표시하는 값을 데이터베이스에 저장하여 카테고리를 구현한다. 대부분의 인터넷 검색이 이런 구현을 설명하고 있다.
하지만 '01/01/01' 이런 방식은 카테고리 생성에 99개로 제한되어 있으며, 또한 카테고리 삽입시 카테고리 표시값을 알아내기 위한 쿼리문이 복잡하며 문자열 파싱을 통해 카테고리의 깊이와 순서를 알아내야 하는 단점이 있다. 이에 데이터베이스 스키마를 간단하게 부모-자식 관계로만 유지하고 표현부는 백트레킹 알고리즘을 사용하여 구현하는 방법으로 카테고리를 구현하였다.
백트레킹 알고리즘은 회귀검사로 재귀함수를 사용하여 경로를 저장하는 방법이다.
1. 데이터베이스 스키마
카테고리_ID : 카테고리의 고유한 인덱스값
카테고리_레벨 : 카테고리의 레벨을 표시(루트는 1, 자식은 1씩 증가)
카테고리_부모값 : 상위 카테고리의 인덱스값
카테고리_이름 : 카테고리의 이름
여기에서 중요한 값은 ID, 레벨, 부모값이다. ID는 고유한 값으로 auto_increment 를 지정해도 무관하다. 또한, 레벨을 페이지 상에서 표시하기 위해 반드시 필요한 값이고 카테고리는 부모값을 가지고 있다. 데이터베이스에 저장되는 데이터는 다음과 같다.
카테고리_ID | 카테고리_레벨 | 카테고리_부모값 | 카테고리_이름
100 | 1 | 1 | 본사 // 루트 카테고리입니다.
101 | 2 | 100 | 청주지점 // 루트 > 청주지점
102 | 2 | 100 | 대전지점 // 루트 > 대전지점
103 | 3 | 101 | 청주1팀 // 루트 > 청주지점 > 청주1팀
104 | 3 | 101 | 청주2팀 // 루트 > 청주지점 > 청주2팀
105 | 3 | 102 | 대전1팀 // 루트 > 대전지점 > 대전1팀
위와 같이 테이블에 데이터가 저장될 것다. 이제 백트래킹 알고리즘을 사용하여 순서대로 출력하는 방법이다.
2. 루트 카테고리를 검색하는 메소드
// 테이블의 레벨 1값을 검색하여 값을 ArrayList<Integer>로 반환
public ArrayList<Integer> getRoots(Connection conn) {
PreparedStatement pst = null;
ResultSet rs = null;
try {
String sql = "SELECT departmentID FROM "+TABLE+" WHERE level = 1";
pst = conn.prepareStatement(sql);
rs = pst.executeQuery();
ArrayList<Integer> list = new ArrayList<Integer>();
while( rs.next() ){
list.add(rs.getInt(1));
}
return list;
} catch(Exception e) {
e.printStackTrace();
return null;
} finally {
JdbcConnector.release(pst);
JdbcConnector.release(rs);
}
}
3. 루트 카테고리를 기준으로 자식 카테고리 리스트를 백트레킹 알고리즘을 사용하여 저장하기
// 카테고리_ID 값을 받아 자식들을 ArrayList<DepartmentVO> 에 순서대로 저장
public void getChildList(Connection conn, int departmentID, ArrayList<DepartmentVO> list) {
PreparedStatement pst = null;
ResultSet rs = null;
try {
String sql = "SELECT * FROM "+TABLE+" WHERE departmentID = ?";
pst = conn.prepareStatement(sql);
pst.setInt(1, departmentID);
rs = pst.executeQuery();
// 부서의 데이터를 저장합니다.
if ( rs.next() ) {
DepartmentVO department = new DepartmentVO();
setDepartment(rs, department);
list.add( department );
}
// 자식들을 검색합니다.
sql = "SELECT departmentID FROM "+TABLE+" WHERE parent = ?";
pst = conn.prepareStatement(sql);
pst.setInt(1, departmentID);
rs = pst.executeQuery();
while ( rs.next() ) {
int parent = rs.getInt(1);
getChildList(conn, parent, list); // 재귀함수를 사용하여 위의 과정을 반복(백트레킹)
}
return ;
} catch(Exception e) {
e.printStackTrace();
} finally {
JdbcConnector.release(rs);
JdbcConnector.release(pst);
}
}
3번의 메소드는 재귀함수를 사용하여 카테고리를 저장한 후 자식 카테고리가 있으면 다시 함수를 호출하여 자식 카테고리를 저장하고 자식-자식 카테고리를 검색하고 있다면 다시 함수를 호출하여 자식-자식 카테고리를 저장하고 자식-자식-자식 카테고리를 검색하는 반복 작업을 계속해서 수행한다.
즉, 다음과 같은 과정을 수행한다.
위의 과정을 거치면서 루트 - 첫번째 자식 - 자식의 첫번째 자식 - ... 이런 방법으로 첫번째부터 순서대로 ArrayList 에 저장을 한다. 자식이 없다면 위의 단계를 거쳐 모든 자식들을 방문한 후 함수는 종료된다.
4. 위의 코드를 합쳐 루트를 기준으로 모든 카테고리를 검색하여 ArrayList 객체로 반환하는 메소드
public ArrayList<DepartmentVO> getList(Connection conn) {
ArrayList<DepartmentVO> childList = new ArrayList<DepartmentVO>();
PreparedStatement pst = null;
ResultSet rs = null;
try {
ArrayList<Integer> rootList = new ArrayList<Integer>();
rootList = getRoots(conn); // 루트 리스트를 가져옴
// 가져온 루트 리스트만큼 반복수행
for ( int i = 0 ; i < rootList.size() ; i++ ) {
int departmentID = rootList.get(i);
getChildList(conn, departmentID, childList); // 루트의 자식을 가져옴
}
return childList;
} catch(Exception e){
e.printStackTrace();
return null;
} finally {
JdbcConnector.release(rs);
JdbcConnector.release(pst);
}
}
위의 getList() 메소드를 실행하여 얻은 결과
이와 같은 방법은 베이터베이스의 테이블은 부모-자식 관계를 이용하고 표현할 때만 재귀함수를 사용한 백크레킹 검색을 하기 때문에 카테고리가 몇 건이건 컴퓨터의 자원이 할달할 수 있을 만큼 카테고리를 생성하거나 자식 카테고리를 늘릴 수 있다.
'자바(Java) > JAVA 2EE' 카테고리의 다른 글
표현언어(Expression Language) - 사용예제 (0) | 2010.05.16 |
---|---|
표현언어(Expression Language) - 문법 (0) | 2010.05.16 |
Lock wait timeout exceeded : try restarting transaction 오류 (0) | 2010.04.29 |
JSP를 사용하여 XML 문서 제작하기 (0) | 2010.04.27 |
서블릿의 시스템 경로 및 요청 경로 구하기 (0) | 2010.04.27 |