반응형
틀 서치 유틸을 이용하여 일반 SQL과 Hibernate의 HQL, Criteria의 검색 조건을 간단하게 처리하는 방법을 살펴본다.
SearchFilter를 이용한 검색 조건 생성
검색 조건이 다양한 형태로 결정되는 경우가 있다. 예를 들어, 검색 조건으로 기간, 이름, 공지여부의 세가지가 있다고 할 경우 다음과 같이 다양한 형태의 검색 조건이 나올 수 있다.
검색 조건이 적은 경우에만 위와 같은 코드가 볼만하지만, 조건이 많아질수록 코드가 복잡해지고 검색 조건이 조금이라도 변경되면 코드에 버그가 생길 가능성이 높아진다. 물론 Hibernate와 같은 API는 Criteria를 통해서 검색 조건을 알맞게 지정해주는 기능이 있지만 HQL을 사용해야 하는 경우는 PreparedStatement를 사용하는 경우와 마찬가지로 복잡한 코드를 만들게 된다.
TLESearchUtil은 PreparedStatement를 사용하든, HQL을 사용하든, 또는 Criteria를 사용하든 검색 조건을 동일한 코드로 지정할 수 있게 함으로써 검색 조건 처리 코드를 쉽게 작성할 수 있도록 도와주는 유틸리티 모듈이다.
TLESearchUtil을 사용하기 위한 준비
TLESearchUtil을 사용하려면 먼저 아래의 순서대로 TLESearchUtil 모듈을 준비해야 한다.
SearchFilter를 사용한 검색 조건 생성
검색 조건은 아래의 두 클래스를 통해서 생성할 수 있다.
검색 조건을 생성할 때에는 위 코드에서 볼 수 있듯이 두가지 단계를 거친다.
(위의 메소드 외에 몇가지 조건들이 더 존재하나 대체적으로 위의 것들이 많이 사용된다. 나머지 메소드들은 다운로드 받은 TLESearchUtil 배포판에 포함된 API 문서를 통해서 확인하기 바란다.)
예를 들어, 최근 30일 이내에 가입한 회원 중에 메일을 수신하고 이메일 주소가 유아시스인 회원 목록을 추출할 때 사용되는 검색 조건을 생성할 때에는 다음과 같이 검색 조건을 지정할 수 있다.
like 검색 모드는 tle.searchutil.Match 인터페이스에 정의된 아래의 상수값을 사용하여 지정한다.
boolean 타입의 처리
자바의 boolean 타입을 DB에 저장할 때에는 일반적으로 다음과 같이 3가지 정도의 방법을 사용한다.
YesNo 클래스, TrueFalse 클래스 그리고 OneZero 클래스는 모두 TRUE와 FALSE 상수필드를 정의하고 있으므로, 위 코드에서처럼 TRUE 또는 FALSE 상수필드를 사용해서 값을 지정할 수 있다.
and와 or의 처리
검색 조건은 and와 or를 통해서 복잡하게 조합될 수 있다. 예를 들어 아래의 조건을 생각해보자.
위와 같이 or 조건이나 and 조건을 표시할 때에는 Restriction 클래스가 제공하는 or()와 and() 메소드를 사용하면 된다. 먼저 여러 조건을 and 연산으로 묶을 때에는 다음과 같이 and() 메소드를 사용해서 지정하면 된다.
Restriction.and() 메소드를 호출하면 tle.searchutil.CondAnd 객체를 리턴하는데, 이 클래스의 addCond() 메소드를 사용해서 연속적으로 and로 묶일 조건을 지정할 수 있다. CondAnd.addCond() 메소드를 통해서 추가될 검색 조건은 SearchFilter.addCond()와 마찬가지로 Restriction 클래스가 제공하는 메소드를 통해서 생성할 수 있다.
or 조건은 Restriction.or() 메소드를 사용하여 지정하며 Restriction.and()와 동일한 방법으로 사용된다. 예를 들면 아래와 같다.
이제 앞서 SQL 쿼리로 표현했던 검색 조건을 SearchFilter를 사용해서 작성해보면 다음과 같다.
filter.addCond(
Restriction.or()
.addCond( // or의 첫번째
Restriction.and()
.addCond(Restriction.ge("REGISTER_DATE", before30day))
.addCond(Restriction.eq("VALID_YN", YesNo.TRUE))
.addCond(Restriction.ge("LOGIN_COUNT", new Integer(20)))
)
.addCond( // or의 두번째
Restriction.and()
.addCond(Restriction.lt("REGISTER_DATE", before30day))
.addCond(Restriction.eq("VALID_YN", YesNo.TRUE))
.addCond(Restriction.ge("LAST_LOGIN_DATE", before1day))
)
);
SearchFilter를 사용한 정렬 조건 생성
SearchFilter.addOrder() 메소드를 사용해서 정렬 순서를 지정할 수 있다. 예를 들어, "REGISTER_DATE" 필드의 내림차순으로 정렬하도록 정보를 추가하고 싶다면 다음과 같은 코드를 사용하면 된다.
Restriction.order(String name, boolean isAsc) 메소드는 정렬 옵션을 생성해주는 데, 두번째 파라미터인 isAsc의 값을 true로 지정하면 오름차순을, false로 지정하면 내림차순을 나타낸다.
여러 필드에 대해서 정렬 조건을 지정하고 싶다면 다음과 같이 SearchFilter.addOrder() 메소드를 정렬 순서대로 호출해주면 된다.
검색 조건을 쿼리에 적용하기
앞서 SearchFilter와 Restriction을 사용해서 검색 조건을 생성하는 방법에 대해서 살펴봤는데, 검색 조건을 만들면 다음으로 해야 할 일은 검색 조건을 사용해서 쿼리를 수행하는 것이다. TLESearchUtil은 현재 다음의 3가지 타입에 대해서 검색 조건을 처리해줄 수 있다.
PreparedStatement에 검색 조건 적용하기
먼저 PreparedStatement를 사용해서 SearchFilter에 저장된 검색 조건을 적용하는 방법을 살펴보자. PreparedStatement와 관련된 클래스는 tle.searchutil.sql.SQLQueryHelper 클래스로서, 이 클래스는 다음의 두 기능을 제공한다.
SQLQueryHelper의 appendWherePart(SearchFilter filter, String alias, StringBuffer buffer) 메소드는 SQL 쿼리의 WHERE 부분에 붙을 쿼리를 생성해서 buffer에 추가해준다. 예를 들어, SearchFilter를 아래와 같이 생성했다고 해보자.
위와 같이 검색 조건을 생성한 경우 helper.appendWherePart() 코드는 아래와 같은 SQL 쿼리를 생성해서 StringBuffer query에 추가한다.
SQL 쿼리에서 테이블 이름에 alias를 지정한 경우 appendWherePart() 메소드를 호출할 때 다음과 같이 alias 값을 전달해주어야 한다.
appendWherePart() 메소드는 파라미터(물음표 ?)에 매핑될 값을 저장한 List를 리턴한다. 이 List는 SQLQueryHelper.setParameter() 메소드에서 파라미터 값을 지정할 때 사용된다. 예를 들면, 아래와 같다.
setParameter() 메소드는 다음과 같이 세개의 인자를 받는다.
SQLQueryHelper.appendWherePart() 메소드로 쿼리를 생성하고, SQLQueryHelper.setParameter() 메소드로 파라미터 값을 지정해주었다면 이제 남은 작업은 PreparedStatement로 쿼리를 실행해서 결과를 사용하는 것 뿐이다.
Hibernate의 Query(HQL)에 검색 조건 적용하기
Hibernate의 HQL은 SQL과 상당히 비슷한 구조를 갖고 있으며 SearchFilter를 사용해서 HQL의 검색 조건 부분을 생성할 수도 있다. 생성하는 방법은 PreparedStatement를 생성하는 과정과 거의 동일하며, 차이점이 있다면 tle.searchutil.sql.SQLQueryHelper 대신에 다음의 두 클래스 중 하나를 사용한다는 점이다.
Hibernate3QueryHelper 클래스를 사용할 때에도 appendWherePart() 메소드와 setParameter() 메소드를 사용한다. 단, Hibernate3QueryHelper 클래스나 Hibernate2QueryHelper 클래스의 setParameter() 메소드는 인덱스 값이 0부터 시작된다는 점에 주의해야 한다. (이는 Hibernate Query의 파라미터 인덱스가 0부터 시작하는 것과 동일하다.)
Hibernate의 Criteria에 검색 조건 적용하기
Hibernate3QueryHelper와 Hibernate2QueryHelper 클래스는 Hibernate의 Criteria를 위한 기능도 제공하고 있으며, 관련 메소드는 아래와 같다.
정렬 순서 적용하기
앞서 SearchFilter.addOrder() 메소드를 사용해서 정렬 순서를 지정하는 방법을 살펴봤는데, 마지막으로 SearchFilter에 저장된 정렬 정보를 사용해서 SQL, Hibernate HQL, 그리고 Hibernate Criteria에서 정렬을 지정하는 방법을 살펴보겠다.
SQL의 order by 부분 생성하기
SQL의 order by 부분을 생성할 때에는 tle.searchutil.sql.SQLQueryHelper 클래스의 appendOrderPart() 메소드를 사용하면 된다. 이 메소드는 다음과 같이 사용된다.
위 코드에서 helper.appendOrderPart() 코드는 다음과 같은 쿼리를 생성하게 된다.
Hibernate HQL의 order by 부분 생성하기
HQL의 order by 부분을 생성하는 코드는 SQL을 사용할 때와 완전히 동일하다. 차이점이라면 SQLQueryHelper 클래스 대신에 Hibernate2QueryHelper 또는 Hibernate3QueryHelper 클래스를 사용한다는 점 뿐이다. 예를 들면 아래와 같다.
Hibernate Criteria에 정렬 순서 적용하기
Hibernate Criteria를 사용하는 경우에는 아래와 같이 Hibernate2QueryHelper 또는 Hibernate3QueryHelper 클래스의 appendOrder() 메소드를 호출하면 정렬 정보가 Criteria에 복사된다.
관련링크:
SearchFilter를 이용한 검색 조건 생성
검색 조건이 다양한 형태로 결정되는 경우가 있다. 예를 들어, 검색 조건으로 기간, 이름, 공지여부의 세가지가 있다고 할 경우 다음과 같이 다양한 형태의 검색 조건이 나올 수 있다.
- 최근 한달간 등록된 공지글
- 3개월 전부터 2개월 전 사이에 등록된 글
- 공지글이거나 또는 등록자 이름이 '관리자'인 글
StringBuffer query = new StringBuffer();
...
if (조건1) {
query.append("name like ?");
}
if (조건2) {
query.append("and register_date between ? and b");
}
pstmt = conn.prepareStatement(query.toString());
int paramIdx = 1;
if (조건1) {
pstmt.setString(paramIdx++, value);
}
if (조건2) {
pstmt.setTimestamp(paramIdx++, fromDate);
pstmt.setTimestamp(paramIdx++, toDate);
}
...
...
if (조건1) {
query.append("name like ?");
}
if (조건2) {
query.append("and register_date between ? and b");
}
pstmt = conn.prepareStatement(query.toString());
int paramIdx = 1;
if (조건1) {
pstmt.setString(paramIdx++, value);
}
if (조건2) {
pstmt.setTimestamp(paramIdx++, fromDate);
pstmt.setTimestamp(paramIdx++, toDate);
}
...
검색 조건이 적은 경우에만 위와 같은 코드가 볼만하지만, 조건이 많아질수록 코드가 복잡해지고 검색 조건이 조금이라도 변경되면 코드에 버그가 생길 가능성이 높아진다. 물론 Hibernate와 같은 API는 Criteria를 통해서 검색 조건을 알맞게 지정해주는 기능이 있지만 HQL을 사용해야 하는 경우는 PreparedStatement를 사용하는 경우와 마찬가지로 복잡한 코드를 만들게 된다.
TLESearchUtil은 PreparedStatement를 사용하든, HQL을 사용하든, 또는 Criteria를 사용하든 검색 조건을 동일한 코드로 지정할 수 있게 함으로써 검색 조건 처리 코드를 쉽게 작성할 수 있도록 도와주는 유틸리티 모듈이다.
TLESearchUtil을 사용하기 위한 준비
TLESearchUtil을 사용하려면 먼저 아래의 순서대로 TLESearchUtil 모듈을 준비해야 한다.
- 틀 프로젝트 사이트(http://kldp.net/projects/tle)에서 TLESearchUtil을 다운로드 받는다.
- 다운로드 받은 TLESearchUtil-1.x.zip 파일의 압축을 푼다.
- 압축을 풀면 생성되는 TLESearchUtil-1.x.jar 파일을 클래스패스에 복사한다.
SearchFilter를 사용한 검색 조건 생성
검색 조건은 아래의 두 클래스를 통해서 생성할 수 있다.
- tle.searchutil.SearchFilter - 검색 조건을 저장한다.
- tle.searchutil.Restriction - SearchFilter에 검색 조건을 추가할 때 사용된다.
SearchFilter filter = new SearchFilter(); filter.addCond(Restriction.eq("BOARD_ID", "1111"));
filter.addCond(Restriction.like("NAME", "최", Match.ANYWHERE);
filter.addCond(Restriction.like("NAME", "최", Match.ANYWHERE);
검색 조건을 생성할 때에는 위 코드에서 볼 수 있듯이 두가지 단계를 거친다.
- 검색 조건을 저장할 SearcFilter 객체를 생성한다.
- Restriction 이 제공하는 메소드를 사용하여 검색 조건을 생성한 뒤, SearchFilter.addCond() 메소드를 호출해서 검색 조건을 추가한다.
- "BOARD_ID" 컬럼 값이 "1111" 이고
- "NAME" 컬럼 값에 "최"를 포함
tle.searchutil.Restriction 클래스가 제공하는 메소드 | |
메소드 | 설명 |
eq(String name, Object value) | 이름이 name인 컬럼의 값이 value인지 검사한다 |
eq(String name, byte value) | 이름이 name인 컬럼의 값이 value인지 검사한다 |
eq(String name, short value) | 이름이 name인 컬럼의 값이 value인지 검사한다 |
eq(String name, int value) | 이름이 name인 컬럼의 값이 value인지 검사한다 |
eq(String name, long value) | 이름이 name인 컬럼의 값이 value인지 검사한다 |
eq(String name, float value) | 이름이 name인 컬럼의 값이 value인지 검사한다 |
eq(String name, double value) | 이름이 name인 컬럼의 값이 value인지 검사한다 |
ne(String name, Object value) | 이름이 name인 컬럼의 값이 value가 아닌지 검사한다 |
ge(String name, Object value) | 이름이 name인 컬럼의 값이 value 보다 크거나 같은 지 검사 |
gt(String name, Object value) | 이름이 name인 컬럼의 값이 value 보다 큰지 검사 |
le(String name, Object value) | 이름이 name인 컬럼의 값이 value 보다 작거나 같은 지 검사 |
lt(String name, Object value) | 이름이 name인 컬럼의 값이 value 보다 작은 지 검사 |
between(String name, Object lo, Object hi) | 이름이 name인 컬럼의 값이 lo와 hi 사이에 존재하는 지 검사 |
in(String name, Collection values) | 이름이 name인 컬럼의 값이 values에 포함된 값 목록에 포함되는 지 검사 |
in(String name, Object[] values) | 이름이 name인 컬럼의 값이 values에 포함된 값 목록에 포함되는 지 검사 |
notIn(String name, Collection values) | 이름이 name인 컬럼의 값이 values에 포함된 값 목록에 포함되지 않는 지 검사 |
notIn(String name, Object[] values) | 이름이 name인 컬럼의 값이 values에 포함된 값 목록에 포함되지 않는 지 검사 |
like(String name, String value) | 이름이 name인 컬럼의 값에 value가 포함되는 지 검사 |
like(String name, String value, int matchMode) | 이름이 name인 컬럼의 값에 value가 포함되는 지 검사. matchMode에 따라 검사 기준이 달라짐 |
isNotNull(String name) | 이름이 name인 컬럼의 값이 null이 아닌지 검사 |
isNull(String name) | 이름이 name인 컬럼의 값이 null인지 검사 |
(위의 메소드 외에 몇가지 조건들이 더 존재하나 대체적으로 위의 것들이 많이 사용된다. 나머지 메소드들은 다운로드 받은 TLESearchUtil 배포판에 포함된 API 문서를 통해서 확인하기 바란다.)
예를 들어, 최근 30일 이내에 가입한 회원 중에 메일을 수신하고 이메일 주소가 유아시스인 회원 목록을 추출할 때 사용되는 검색 조건을 생성할 때에는 다음과 같이 검색 조건을 지정할 수 있다.
SearchFilter filter = new SearchFilter();
Calendar cal = Calendar.getInstance();
cal.set(Calendar.DATE, -30);
filter.addCond( Restriction.gt("REGISTER_DATE", cal.getTime()) );
filter.addCond( Restriction.eq("MAILING_YN", YesNo.TRUE) );
filter.addCond( Restrictoin.like("EMAIL", "@uasis.com", Match.END) );
Calendar cal = Calendar.getInstance();
cal.set(Calendar.DATE, -30);
filter.addCond( Restriction.gt("REGISTER_DATE", cal.getTime()) );
filter.addCond( Restriction.eq("MAILING_YN", YesNo.TRUE) );
filter.addCond( Restrictoin.like("EMAIL", "@uasis.com", Match.END) );
like 검색 모드는 tle.searchutil.Match 인터페이스에 정의된 아래의 상수값을 사용하여 지정한다.
- ANYWHERE - 문장을 포함
- START - 문장으로 시작
- END - 문장으로 시작
- EXACT - 정확하게 일치
filter.addCond( Restriction.like("NAME", "최", Match.START) );
boolean 타입의 처리
자바의 boolean 타입을 DB에 저장할 때에는 일반적으로 다음과 같이 3가지 정도의 방법을 사용한다.
- true를 'Y'로, false를 'N'으로 저장한다. - Yes No 스타일
- true를 'T'로, false를 'F'으로 저장한다. - True False 스타일
- true를 1로, false를 0 으로 저장한다. - 숫자 스타일
- tle.searchutil.YesNo - Yes No 스타일로 true/false 값을 지정할 때 사용된다.
- tle.searchutil.TrueFalse - True False 스타일로 true/false 값을 지정할 때 사용된다.
- tle.searchutil.OneZero- 숫자 스타일로 true/false 값을 지정할 때 사용된다.
filter.addCond( Restriction.eq("MAILING_YN", YesNo.TRUE) );
filter.addCond( Restriction.eq("MALE_TF", TrueFalse.FALSE) );
filter.addCond( Restriction.eq("MALE_TF", TrueFalse.FALSE) );
YesNo 클래스, TrueFalse 클래스 그리고 OneZero 클래스는 모두 TRUE와 FALSE 상수필드를 정의하고 있으므로, 위 코드에서처럼 TRUE 또는 FALSE 상수필드를 사용해서 값을 지정할 수 있다.
and와 or의 처리
검색 조건은 and와 or를 통해서 복잡하게 조합될 수 있다. 예를 들어 아래의 조건을 생각해보자.
- 가입일이 30일 이내이면서, 유효한 회원이고, 로그인 회수가 20회 이상인 회원
- 또는
- 가입일이 30일 이전이면서, 유효한 회원이고, 최근 로그인 시간이 1루 이내인 회원
(
REGISTER_DATE >= '2005-07-25' and
VALID_YN = 'Y' and
LOGIN_COUNT >= 20
)
or
(
REGISTER_DATE <= '2005-07-24' and
VALID_YN = 'Y' and
LAST_LOGIN_DATE >= '2005-08-24
)
REGISTER_DATE >= '2005-07-25' and
VALID_YN = 'Y' and
LOGIN_COUNT >= 20
)
or
(
REGISTER_DATE <= '2005-07-24' and
VALID_YN = 'Y' and
LAST_LOGIN_DATE >= '2005-08-24
)
위와 같이 or 조건이나 and 조건을 표시할 때에는 Restriction 클래스가 제공하는 or()와 and() 메소드를 사용하면 된다. 먼저 여러 조건을 and 연산으로 묶을 때에는 다음과 같이 and() 메소드를 사용해서 지정하면 된다.
filter.addCond(
Restriction.and()
.addCond( Restriction.ge("REGISTER_DATE", day10) )
.addCond( Restriction.eq("VALID_YN", YesNo.TRUE) )
.addCond( Restriction.ge("LOGIN_COUNT", 20) )
);
Restriction.and()
.addCond( Restriction.ge("REGISTER_DATE", day10) )
.addCond( Restriction.eq("VALID_YN", YesNo.TRUE) )
.addCond( Restriction.ge("LOGIN_COUNT", 20) )
);
Restriction.and() 메소드를 호출하면 tle.searchutil.CondAnd 객체를 리턴하는데, 이 클래스의 addCond() 메소드를 사용해서 연속적으로 and로 묶일 조건을 지정할 수 있다. CondAnd.addCond() 메소드를 통해서 추가될 검색 조건은 SearchFilter.addCond()와 마찬가지로 Restriction 클래스가 제공하는 메소드를 통해서 생성할 수 있다.
or 조건은 Restriction.or() 메소드를 사용하여 지정하며 Restriction.and()와 동일한 방법으로 사용된다. 예를 들면 아래와 같다.
filter.addCond(
Restriction.or()
.addCond( Restriction.ge("REGISTER_DATE", day10) )
.addCond( Restriction.eq("VALID_YN", YesNo.TRUE) )
Restriction.or()
.addCond( Restriction.ge("REGISTER_DATE", day10) )
.addCond( Restriction.eq("VALID_YN", YesNo.TRUE) )
이제 앞서 SQL 쿼리로 표현했던 검색 조건을 SearchFilter를 사용해서 작성해보면 다음과 같다.
filter.addCond(
Restriction.or()
.addCond( // or의 첫번째
Restriction.and()
.addCond(Restriction.ge("REGISTER_DATE", before30day))
.addCond(Restriction.eq("VALID_YN", YesNo.TRUE))
.addCond(Restriction.ge("LOGIN_COUNT", new Integer(20)))
)
.addCond( // or의 두번째
Restriction.and()
.addCond(Restriction.lt("REGISTER_DATE", before30day))
.addCond(Restriction.eq("VALID_YN", YesNo.TRUE))
.addCond(Restriction.ge("LAST_LOGIN_DATE", before1day))
)
);
SearchFilter를 사용한 정렬 조건 생성
SearchFilter.addOrder() 메소드를 사용해서 정렬 순서를 지정할 수 있다. 예를 들어, "REGISTER_DATE" 필드의 내림차순으로 정렬하도록 정보를 추가하고 싶다면 다음과 같은 코드를 사용하면 된다.
SearchFilter filter = new SearchFilter();
... // 검색 조건 지정
filter.addOrder( Restriction.order("REGISTER_DATE", false) );
... // 검색 조건 지정
filter.addOrder( Restriction.order("REGISTER_DATE", false) );
Restriction.order(String name, boolean isAsc) 메소드는 정렬 옵션을 생성해주는 데, 두번째 파라미터인 isAsc의 값을 true로 지정하면 오름차순을, false로 지정하면 내림차순을 나타낸다.
여러 필드에 대해서 정렬 조건을 지정하고 싶다면 다음과 같이 SearchFilter.addOrder() 메소드를 정렬 순서대로 호출해주면 된다.
SearchFilter filter = new SearchFilter();
// 순서대로 정렬 조건을 지정할 수 있다.
filter.addOrder( Restriction.order("NAME", true) );
filter.addOrder( Restriction.order("BIRTH", false) );
// 순서대로 정렬 조건을 지정할 수 있다.
filter.addOrder( Restriction.order("NAME", true) );
filter.addOrder( Restriction.order("BIRTH", false) );
검색 조건을 쿼리에 적용하기
앞서 SearchFilter와 Restriction을 사용해서 검색 조건을 생성하는 방법에 대해서 살펴봤는데, 검색 조건을 만들면 다음으로 해야 할 일은 검색 조건을 사용해서 쿼리를 수행하는 것이다. TLESearchUtil은 현재 다음의 3가지 타입에 대해서 검색 조건을 처리해줄 수 있다.
- java.sql.PrepareStatement
- Hibernate의 Query
- Hibernate의 Criteria
PreparedStatement에 검색 조건 적용하기
먼저 PreparedStatement를 사용해서 SearchFilter에 저장된 검색 조건을 적용하는 방법을 살펴보자. PreparedStatement와 관련된 클래스는 tle.searchutil.sql.SQLQueryHelper 클래스로서, 이 클래스는 다음의 두 기능을 제공한다.
- PreparedStatement에서 사용되는 SQL 쿼리에서 where 부분에 붙을 SearchFilter에서 지정한 조건에 맞는 쿼리를 생성한다.
- 쿼리의 파라미터(물음표 ?)에 삽입될 값을 지정한다.
SearchFilter filter = new SearchFilter();
... // filter.addCond()를 통해서 검색 조건 생성
SQLQueryHelper helper = new SQLQueryHelper();
try {
StringBuffer query = new StringBuffer();
query.append("select * from MEMBER");
List paramValues = null;
if (filter.hasCond()) { // 검색 조건이 있는지의 여부 검사
query.append(" where ");
// 검색 조건과 관련된 쿼리를 생성한다
paramValues = helper.appendWherePart(filter, null, query);
}
pstmt = conn.prepareStatement(query.toString());
if (filter.hasConds()) helper.setParameter(pstmt, 1, paramValues);
rs = pstmt.executeQuery();
...
} finally {
if (pstmt != null) try { pstmt.close(); } catch(SQLException ex) {}
...
}
... // filter.addCond()를 통해서 검색 조건 생성
SQLQueryHelper helper = new SQLQueryHelper();
try {
StringBuffer query = new StringBuffer();
query.append("select * from MEMBER");
List paramValues = null;
if (filter.hasCond()) { // 검색 조건이 있는지의 여부 검사
query.append(" where ");
// 검색 조건과 관련된 쿼리를 생성한다
paramValues = helper.appendWherePart(filter, null, query);
}
pstmt = conn.prepareStatement(query.toString());
if (filter.hasConds()) helper.setParameter(pstmt, 1, paramValues);
rs = pstmt.executeQuery();
...
} finally {
if (pstmt != null) try { pstmt.close(); } catch(SQLException ex) {}
...
}
SQLQueryHelper의 appendWherePart(SearchFilter filter, String alias, StringBuffer buffer) 메소드는 SQL 쿼리의 WHERE 부분에 붙을 쿼리를 생성해서 buffer에 추가해준다. 예를 들어, SearchFilter를 아래와 같이 생성했다고 해보자.
SearchFilter filter = new SearchFilter();
filter.addCond( Restriction.eq("MAILING_YN", YesNo.TRUE) );
filter.addCond( Restriction.eq("MALE_TF", TrueFalse.FALSE) );
StringBuffer query = new StringBuffer();
...
SQLQueryHelper helper = new SQLQueryHelper();
List paramValues = helper.appendWherePart(filter, null, query);
filter.addCond( Restriction.eq("MAILING_YN", YesNo.TRUE) );
filter.addCond( Restriction.eq("MALE_TF", TrueFalse.FALSE) );
StringBuffer query = new StringBuffer();
...
SQLQueryHelper helper = new SQLQueryHelper();
List paramValues = helper.appendWherePart(filter, null, query);
위와 같이 검색 조건을 생성한 경우 helper.appendWherePart() 코드는 아래와 같은 SQL 쿼리를 생성해서 StringBuffer query에 추가한다.
(MAILING_YN = ? and MALE_TF = ?)
SQL 쿼리에서 테이블 이름에 alias를 지정한 경우 appendWherePart() 메소드를 호출할 때 다음과 같이 alias 값을 전달해주어야 한다.
StringBuffer query = new StringBuffer();
query.append("select * from MEMBER mem ");
...
helper.appendWherePart(filter, "mem", query);
query.append("select * from MEMBER mem ");
...
helper.appendWherePart(filter, "mem", query);
appendWherePart() 메소드는 파라미터(물음표 ?)에 매핑될 값을 저장한 List를 리턴한다. 이 List는 SQLQueryHelper.setParameter() 메소드에서 파라미터 값을 지정할 때 사용된다. 예를 들면, 아래와 같다.
StringBuffer query = new StringBuffer();
...
SQLQueryHelper helper = new SQLQueryHelper();
List paramValues = null;
if ( filter.hasConds() ) {
..
paramValues = helper.appendWherePart(filter, null, query);
}
...
pstmt = conn.prepareStatement(query.toString());
if (filter.hasConds()) helper.setParameter(pstmt, 1, paramValues);
...
SQLQueryHelper helper = new SQLQueryHelper();
List paramValues = null;
if ( filter.hasConds() ) {
..
paramValues = helper.appendWherePart(filter, null, query);
}
...
pstmt = conn.prepareStatement(query.toString());
if (filter.hasConds()) helper.setParameter(pstmt, 1, paramValues);
setParameter() 메소드는 다음과 같이 세개의 인자를 받는다.
- 값을 매핑할 PreparedStatement
- 매핑할 파라미터의 시작 인덱스
- 파라미터에 매핑될 값을 저장한 List
query.append("where VALID = ? and REGISTER_DATE > ?");
if ( filter.hasConds() ) {
query.append(" and ");
paramValues = helper.appendWherePart(filter, null, query);
}
...
if (filter.hasConds()) helper.setParameter(pstmt, 3, paramValues);
if ( filter.hasConds() ) {
query.append(" and ");
paramValues = helper.appendWherePart(filter, null, query);
}
...
if (filter.hasConds()) helper.setParameter(pstmt, 3, paramValues);
SQLQueryHelper.appendWherePart() 메소드로 쿼리를 생성하고, SQLQueryHelper.setParameter() 메소드로 파라미터 값을 지정해주었다면 이제 남은 작업은 PreparedStatement로 쿼리를 실행해서 결과를 사용하는 것 뿐이다.
Hibernate의 Query(HQL)에 검색 조건 적용하기
Hibernate의 HQL은 SQL과 상당히 비슷한 구조를 갖고 있으며 SearchFilter를 사용해서 HQL의 검색 조건 부분을 생성할 수도 있다. 생성하는 방법은 PreparedStatement를 생성하는 과정과 거의 동일하며, 차이점이 있다면 tle.searchutil.sql.SQLQueryHelper 대신에 다음의 두 클래스 중 하나를 사용한다는 점이다.
- tle.searchutil.hibernate2.Hibernate2QueryHelper - Hibernate 2.1.x 버전 용
- tle.searchutil.hibernate3.Hibernate3QueryHelper - Hibernate 3.0.x 버전 용
SearchFilter filter = new SearchFilter();
// 테이블의 컬럼 이름 대신 매핑되는 객체의 프로퍼티 이름을 사용한다.
filter.addCond( Restriction.eq("valid", YesNo.TRUE) );
filter.addCond( Restriction.eq("registerDate", YesNo.TRUE) );
...
Hibernate3QueryHelper helper = new Hibernate3QueryHelper();
List paramValues = null;
StringBuffer query = new StringBuffer();
query.append("from Member m ");
if ( filter.hasConds() ) {
paramValues = helper.appendWherePart(filter, "m", query);
}
Query query = session.createQuery(Member.class);
if ( filter.hasConds() ) helper.setParameter(query, 0, paramValues);
// 테이블의 컬럼 이름 대신 매핑되는 객체의 프로퍼티 이름을 사용한다.
filter.addCond( Restriction.eq("valid", YesNo.TRUE) );
filter.addCond( Restriction.eq("registerDate", YesNo.TRUE) );
...
Hibernate3QueryHelper helper = new Hibernate3QueryHelper();
List paramValues = null;
StringBuffer query = new StringBuffer();
query.append("from Member m ");
if ( filter.hasConds() ) {
paramValues = helper.appendWherePart(filter, "m", query);
}
Query query = session.createQuery(Member.class);
if ( filter.hasConds() ) helper.setParameter(query, 0, paramValues);
Hibernate3QueryHelper 클래스를 사용할 때에도 appendWherePart() 메소드와 setParameter() 메소드를 사용한다. 단, Hibernate3QueryHelper 클래스나 Hibernate2QueryHelper 클래스의 setParameter() 메소드는 인덱스 값이 0부터 시작된다는 점에 주의해야 한다. (이는 Hibernate Query의 파라미터 인덱스가 0부터 시작하는 것과 동일하다.)
Hibernate의 Criteria에 검색 조건 적용하기
Hibernate3QueryHelper와 Hibernate2QueryHelper 클래스는 Hibernate의 Criteria를 위한 기능도 제공하고 있으며, 관련 메소드는 아래와 같다.
- appendCriterion(Criteria crit, SearchFilter filter)
SearchFilter filter = new SearchFilter();
filter.addCond(...);
filter.addCond(...);
Hibernate3QueryHelper helper = new Hibernate3QueryHelper();
Criteria crit = session.createCriteria(Member.class);
if ( filter.hasConds() ) helper.appendCriterion(crit, filter);
List list = crit.list(); // 검색 조건에 해당하는 객체 목록 리턴
filter.addCond(...);
filter.addCond(...);
Hibernate3QueryHelper helper = new Hibernate3QueryHelper();
Criteria crit = session.createCriteria(Member.class);
if ( filter.hasConds() ) helper.appendCriterion(crit, filter);
List list = crit.list(); // 검색 조건에 해당하는 객체 목록 리턴
정렬 순서 적용하기
앞서 SearchFilter.addOrder() 메소드를 사용해서 정렬 순서를 지정하는 방법을 살펴봤는데, 마지막으로 SearchFilter에 저장된 정렬 정보를 사용해서 SQL, Hibernate HQL, 그리고 Hibernate Criteria에서 정렬을 지정하는 방법을 살펴보겠다.
SQL의 order by 부분 생성하기
SQL의 order by 부분을 생성할 때에는 tle.searchutil.sql.SQLQueryHelper 클래스의 appendOrderPart() 메소드를 사용하면 된다. 이 메소드는 다음과 같이 사용된다.
SearchFilter filter = new SearchFilter();
filter.addOrder( Restriction.order("NAME", true) );
filter.addOrder( Restriction.order("AGE", false) );
StringBuffer query = new StringBuffer();
query.append("select * from MEMBER m");
...
SQLQueryHelper helper = new SQLQueryHelper();
if ( filter.hasOrders() ) { // 정렬 순서를 지정했는지 여부
query.append(" order by ");
helper.appendOrderPart(filter, "m", query);
}
filter.addOrder( Restriction.order("NAME", true) );
filter.addOrder( Restriction.order("AGE", false) );
StringBuffer query = new StringBuffer();
query.append("select * from MEMBER m");
...
SQLQueryHelper helper = new SQLQueryHelper();
if ( filter.hasOrders() ) { // 정렬 순서를 지정했는지 여부
query.append(" order by ");
helper.appendOrderPart(filter, "m", query);
}
위 코드에서 helper.appendOrderPart() 코드는 다음과 같은 쿼리를 생성하게 된다.
m.NAME asc, m.AGE desc
Hibernate HQL의 order by 부분 생성하기
HQL의 order by 부분을 생성하는 코드는 SQL을 사용할 때와 완전히 동일하다. 차이점이라면 SQLQueryHelper 클래스 대신에 Hibernate2QueryHelper 또는 Hibernate3QueryHelper 클래스를 사용한다는 점 뿐이다. 예를 들면 아래와 같다.
SearchFilter filter = new SearchFilter();
filter.addOrder( Restriction.order("NAME", true) );
filter.addOrder( Restriction.order("AGE", false) );
StringBuffer query = new StringBuffer();
query.append("select * from MEMBER m");
...
Hibernate3QueryHelper helper = new Hibernate3QueryHelper();
if ( filter.hasOrders() ) { // 정렬 순서를 지정했는지 여부
query.append(" order by ");
helper.appendOrderPart(filter, "m", query);
}
...
filter.addOrder( Restriction.order("NAME", true) );
filter.addOrder( Restriction.order("AGE", false) );
StringBuffer query = new StringBuffer();
query.append("select * from MEMBER m");
...
Hibernate3QueryHelper helper = new Hibernate3QueryHelper();
if ( filter.hasOrders() ) { // 정렬 순서를 지정했는지 여부
query.append(" order by ");
helper.appendOrderPart(filter, "m", query);
}
...
Hibernate Criteria에 정렬 순서 적용하기
Hibernate Criteria를 사용하는 경우에는 아래와 같이 Hibernate2QueryHelper 또는 Hibernate3QueryHelper 클래스의 appendOrder() 메소드를 호출하면 정렬 정보가 Criteria에 복사된다.
SearchFilter filter = new SearchFilter();
filter.addOrder( Restriction.order("NAME", true) );
filter.addOrder( Restriction.order("AGE", false) );
Hibernate3QueryHelper helper = new Hibernate3QueryHelper();
Criteria crit = session.createCriteria(Member.class);
if (filter != null && filter.hasOrders()) helper.appendOrder(crit, filter);
filter.addOrder( Restriction.order("NAME", true) );
filter.addOrder( Restriction.order("AGE", false) );
Hibernate3QueryHelper helper = new Hibernate3QueryHelper();
Criteria crit = session.createCriteria(Member.class);
if (filter != null && filter.hasOrders()) helper.appendOrder(crit, filter);
관련링크: