2011년 5월 31일 화요일

iBATIS in Action (8/14)

1. 동적인 SQL 사용하기

1.1. 동적인 WHERE 조건절 다루기

다음 예제에서 장바구니 어플리케이션의 Category 테이블을 조회한다. parentCategoryId 칼럼은 자기 자신을 참조하는 칼럼이다. 다시 말하면 parentCategoryId 그림에서 보듯이 동일한 Category 테이블의 categoryId 참조한다.

요구사항은 간단하다. 전달받은 Category객체의 parentCategoryId 프로퍼티가 null 이면 이것은 가장 상위 레벨의 카테고리를 모두 조회한다는 의미이다.

parentCategoryId 프로퍼티가 null 아닌 숫자 값이라면 부모 category 자식 category 객체를 모두 조회한다는 것을 뜻한다. 부모 category parentCategoryId 값을 의미한다.

SQL 에서는 =기호를 null 비교에 사용할 있다. NULL값인지 여부는 IS 키워드로 확인해야 한다. 우리는 NULL인지 혹은 NULL 아닌지를 비교하기 위해 동일한 SQL 구문을 사용하고자 한다. 이를 하나의 매핑 구문으로 처리하기 위해서 동적 SQL 사용할 것이다. 동적 SQL 뼈대를 알아보기 위해 다음의 매핑 구문을 사용할 것이다.

동적 SQL 뼈대는 언제나 부모 요소로 시작한다. 부모 요소는 동적 SQL 요소 어떤 것이라도 있다. 코드의 경우 부모 요소로 사용했다. 요소는 다른 동적 SQL 요소들과는 달리 어떤 값이나 상태도 평가하지 안는다. 요소는 대개 prepend 속성만을 사용하며, prepend 속성의 값은 요소의 결과가 되는 내용의 가장 앞에 오게 된다. 예제에서 prepend 속성의 값인 WHERE 내포하고 있는 동적 SQL 요소를 처리하면 생성되는 결과 SQL 가장 앞에 붙을 것이다.

부모 요소의 몸체에는 간단한 SQL 문법이나 다른 동적 SQL 요소가 온다. 예제에서 요소의 몸체에 내포된 요소를 있다. 여기서 우리가 관심을 가져야 부분은 Category 파라미터 클래스(parameterClass = “Category”) 가진 parentCategoryId 프로퍼티의 값이 null인지 아닌지 여부에 따라 WHERE 조건절에 적절한 SQL 구문을 넣는 것이다.

시점에서, 언제 prepend 속성의 값이 다른 SQL 구문 앞에 출력하게 되는지 알고 있어야 한다. 요소의 몸체에 아무런 텍스트도 나오지 않으면 prepend 값은 무시될 것이다. Prepend 속성 값이 앞에 출력되려면, 뒤에 붙은 SQL문이 있어야만 한다. 우리의 예제에서는 결과 SQL 항상 존재한다. 요소의 내용으로 아무것도 생성되지 않는 경우에는 prepend 속성의 WHERE 간단히 무시된다.

동적 SQL 이점 하나는 SQL 코드의 재사용성을 높여준다는 것이다. 예제에서 동적 SQL 사용하지 않고서 위와 같은 기능을 구현하려면 개의 select 구문을 작성해야 했을 것이다. 게다가 DAO계층에서 category 객체의 parentCategoryId 값을 먼저 확인하고, parentCategoryId값이 null인지 아닌지 여부에 따라 적당한 매핑 구문을 선택해서 호출해야 한다. 비록 이번 예제는 간단해서 동적 SQL 없더라도 별로 불편할 없지만, 여러 서로 다른 프로퍼티의 조합으로 인해 SQL구문이 기하급수적으로 증가할 가능성이 있을 경우에는 동적 SQL 진정한 가치가 분명하게 드러난다. 동적 SQL 사용하여 매핑 구문을 재사용하도록 하고 정적인 SQL 구문을 여러 작성하는 것은 피하도록 한다.

이제 동적 SQL 사용법과 강력함에 대해 이해했을 것이다. 모든 요소와 속성을 상세히 알아보자.

1.2. 동적 요소들과 친숙해지기

iBATIS 강력한 XML 요소들을 통해 동적 SQL문제를 처리한다. 요소들은 매핑 구문에 넘어온 파라미터 객체에 관한 다양한 조건들을 평가하는 역할을 한다. 존재하는 모든 요소들을 이해하고 그것들이 올바르게 SQL 출력하는 어떤 역할을 하는지 이해하고 있어야 한다. 다음 절에서는 요소들을 , 이항연산, 단항연산, 파라미터 그리고 5 카테고리로 나눈다. 각각의 그룹은 공통적인 특성을 공유하는 하나 이상의 관련 XML요소를 포함하고 있다. 그룹을 알아보기 전에 잠시 시간을 할애해서 모든 동적 SQL 요소가 공유하는 가지 공통적인 속성과 행위를 알아보자.

모든 동적 요소는 prepend, open, close 속성을 포함한다. Open close속성은 각각의 요소에서 똑같이 작동한다. 개의 속성은 동적으로 생성된 결과 내용의 시작이나 끝에 무조건 값은 붙인다. Prepend 속성은 요소를 제외하고 모든 요소에서 동일하게 작동한다. 요소는 내용물을 만들어낼 때는 항상 prepend 값을 앞에 붙인다. 요소에서 prepend 지정된 값의 처리를 막을 있는 방법은 없다. 다음은 가지 동적 SQL 요소의 실례를 보여준다.

----(1) 요소 시작

----(2) 간단한 요소

y=#y#

복잡한 요소

prepend="AND" open="(" close=")">

----(4) 내포된 요소

a=#x.a#

a=#x.b#

a=#x.c#

. (1)에서 시작 요소는 자식 요소의 첫번째 prepend 기능을 자동으로 무조건 제거한다. 요소가 true 된다면 (2) removeFirstPrepend 기능이 요소 때문에 자동으로 작동하게 된다. 동일한 레벨의 뒤따라오는 모든 요소들은 각각의 내용에 앞에 붙을 prepend 값을 가질 것이다. 요소는 (3)removeFirstPrepend 속성을 명시하고 있다. Open close 속성 값은 요소에서 출력된 내용의 앞뒤를 감싼다. (4)에서 번째 내포된 내용을 만드는 요소는 removeFirstPrepend 요구사항을 처리할 것이다. 처음으로 내용을 출력하는 요소는 prepend 속성의 OR 출력하지 않는다. 이로 인해 괄호 안에 정확한 SQL 생성된다.

번째 prepend 제거 기능은 모든 요소에서 자동으로 혹은 명시적 지정을 통해 사용할 있다. 요소는 이것을 자동으로 지원한다. 다른 요소들은 이를 removeFirstPrepend속성을 사용하여 명시함으로써 지원한다. 번째 prepend 제거 기능은 처음으로 내용을 생성하는 자식 요소의 prepend 기능을 제거해 버린다. 만약 처음으로 내용을 생성하는 자식 요소가 prepend 속성을 가지고 있지 않다면 removeFirstPrepend 기능을 수행하지 않은 것로 간주한다. 그래서 이후에 내용을 생성하는 자식요소들 prepend 속성이 있을 때까지 removeFirstPrepend 제거 기능은 유효하고 이후 처음으로 나오는 prepend 속성에 대해 작동한다.

공통 기능의 마지막 부분은 모든 요소를 서로 독립적으로 사용할 있다는 것이다. 이것은 요소 내부에서 모든 동적 SQL 요소를 포함시켜 사용할 필요가 없다는 의미이다. 요소로 시작해서 안에 요소를 넣는 것도 요소로 이들을 감싸는 것만큼 쉽게 있다. 이러한 기능을 제공하는 이유는 요소가 필요한 경우는 단지 요소의 open, close 혹은 prepend 속성값을 출력 내용에 붙여주기 위함일 뿐이기 때문이다.

그럼 이제부터 요소들의 카테고리 각각을 분석해보자.

8.2.1 요소

최상위 레벨에서만 사용할 있는 요소이다. 요소는 다른 요소 안에 들어갈 없다는 뜻이다. 요소를 이용해 동적 SQL 절들을 구분한다. 요소는 결과 내용에 공통 속성인 prepend, open 혹은 close 통해 뒤에 뭔가를 출력해 주기 위해서 사용한다.

이제 요소에서 사용할 있는 속성들을 모두 살펴보았다. 다음은 요소 사용법을 보여준다.

여기서 동적인 SQL 사용해서, parentCategoryId 프로퍼티를 검사하여 SQL 생성하는 select 구문의 WHERE절을 구성하였다.

8.2.2 이항연산 요소

이항연산 요소는 파라미터 프로퍼티의 값을 다른 값이나 다른 파라미터 프로퍼티 값과 비교한다. 요소의 내용은 비교 결과가 true 사용된다. 모든 이항연산 요소는 property, compareProperty, compareValue 속성을 공통으로 가지고 있다. Property 속성은 비교할 기본값을 제공한다. compareProperty compareValue 비교 대상이 되는 값을 제공한다. compareProperty 속성에는 기본값에 대해 비교할 값을 가지고 있는 파라미터 객체의 프로퍼티를 명시한다. compareValue 에는 기본값에 대해 비교할 정적인 값을 명시한다. 요소의 이름은 값의 비교 방법을 의미한다. 요소의 속성은 표에서 있다.

Property (필수)

compareValue compareProperty 비교하는데 사용할 파라미터 프로퍼티

Prepend (선택적)

값은 보통 요소 내용의 가장 앞에 출력된다. 다음의 경우에는 prepend 값이 출력되지 않고 무시된다. (a) 요소의 결과 내용이 공백인 경우, (b)요소가 처음으로 내용을 만들어 냈고 removeFirstPrepend 속성이 true 요소에 내포되어 있는 경우, (c) 요소 다음에 공백이 아닌 내용을 처음으로 출력하고 prepend 값이 지정돼 있는 요소일 경우.

Open (선택적)

Open 값은 요소 내용에 접두사로 붙이기 위해 사용한다. Open 값은 요소의 결과 내용이 공백일 때는 접두사로 붙지 않는다. Open 값은 prepend 속성 값보다는 뒤에 붙는다. 예를 들어 prepend=”OR” 이고 open=”(“ 라면 조합된 결과는 “OR(“ 것이다.

Close(선택적)

Close 값은 요소 결과 내용의 뒤에 덧붙이는데 사용한다. 값은 요소 내용이 공백이라면 덧붙여지지 않을 것이다.

removeFirstPrepend( 선택적)

값은 처음으로 내용을 출력하는 자식 요소의 prepend 속성 값을 출력할지 여부를 결정한다.

compareProperty(compareValue 명시되지 않았다면 필수)

compareProperty property 속성에 의해 명명된 프로퍼티와 비교할 파라미터 객체의 프로퍼티를 명시한다.

compareValue(compareProperty 명시되지 않았다면 필수)

Property 속성에 의해 명명된 프로퍼티와 비교할 정적인 비교값

모든 이항연산 동적 요소는 위의 속성들을 공유하고, 요소 이름 자체는 다음의 표에서 있다.

Property 속성값이 compareProperty 값이나 compareValue 값과 같은지 검사

Property 속성값이 compareProperty 값이나 compareValue 값과 같지 않은지 검사

Property 속성값이 compareProperty 값이나 compareValue 값보다 큰지 검사

Property 속성값이 compareProperty 값이나 compareValue 값보다 크거나 같은지 검사

Property 속성값이 compareProperty 값이나 compareValue 값보다 작은지 검사

Property 속성값이 compareProperty 값이나 compareValue 값보다 작거나 같은지 검사

표는 좋은 참조가 되겠지만 예제가 충분치 않다. 아래에서 어떻게 요소들을 함께 사용하는지 보자.

에서 select 구문을 생성하고 사용할 shippingType 값이 100보다 작은 standard 타입인지, 100보다 freight 타입인지 결정하기 위해 weight 프로퍼티를 검사한다.

8.2.3 단항연산 요소

단항연산 동적 요소는 빈즈 프로퍼티의 상태를 검사만하고 다른 값에 대한 비교는 수행하지 않는다. 상태의 결과가 true 라면 내용이 포함된다. 모든 단항연산 요소는 property 속성을 공유한다. Property 속성에는 상태를 검사할 파라미터 객체의 프로퍼티 이름을 지정해준다.요소의 이름은 어떤 상태를 검사할지를 의미한다. 표는 단항연산 요소의 속성을 보여준다.

Property(필수)

상태 비교를 위해 사용되는 파라미터의 변수명

Prepend(필수)

값은 요소 내용의 가장 앞에 출력된다. 다음의 경우에는 prepend 값이 출력되지 않고 무시된다. (a) 요소의 결과 내용이 공백인 경우, (b) 요소가 처음으로 내용을 만들어 냈고 removeFirstPrepend 속성이 true 요소에 내포되어 있는 경우, (c) 요소 다음에 공백이 아닌 내용을 처음으로 출력하고 prepend 값이 지정돼 있는 요소일 경우

Open(선택적)

Open 값은 요소 내용에 접두사로 붙이기 위해 사용한다. Open 값은 요소의 결과 내용이 공백일 때는 접두사로 붙지 않는다. open값은 prepend 속성값보다는 뒤에 붙는다. 예를 들어, prepend=”OR” 이고 open=”(“ 라면 조합된 결과는 “OR (“ 것이다.

Close(선택적)

Close 값은 요소 결과 내용의 뒤에 덧붙이는데 사용한다. 값은 요소 내용이 공백이라면 덧붙여지지 않을 것이다.

removeFirstPrepend(선택적)

값으로 처음으로 내용을 출력하는 자식 요소의 prepend 속성 값을 출력할지 여부를 결정한다.

표의 모든 속성은 아래의 표에 있는 모든 단항연산 동적 SQL 요소에서 사용 가능하다.

명시된 프로퍼티가 파라미터에 존재하는지 검사. 빈즈에서는 프로퍼티를 찾고 Map에서는 키를 찾는다.

명시된 프로퍼티가 파라미터에 존재하지 않는지 검사. 빈즈에서는 프로퍼티를 찾고 Map에서는 키를 찾는다.

명시된 프로퍼티가 null 인지 검사. 빈즈에서는 프로퍼티를 찾고 Map에서는 key 찾는다. 키가 존재하지 않으면 true 반환한다.

명시된 프로퍼티가 null 아닌 다른 값인지를 검사. 빈즈에서는 프로퍼티를 찾고 Map에서는 키를 찾는다. 키가 존재하지 않는다면 false 반환한다.

명시된 프로퍼티가 null 이거나 문자열(“”), 컬렉션이나 String, valueOf() 인지를 검사

명시된 프로퍼티가 null 아니거나 문자열(“”), 컬렉션, String, valueOf() 아닌지 검사

아래는 단항연산 동적 SQL 요소를 어떻게 사용하는지 보여준다.

여기서는 간단한 select 매핑 구문을 생성하고, 동적 SQL 요소를 사용하여 productType 프로퍼티의 값에 따라 선택적으로 결과를 걸러서 가져온다.

8.2.4 파라미터 요소

파라미터 없이 매핑 구문을 정의하는 것도 가능하다. 파라미터 요소는 파라미터가 매핑 구문으로 전달되는지를 검사하기 위해 만들어졌다. 표는 요소의 속성을 보여준다.

Prepend(선택적)

값은 요소 내용의 가장 앞에 출력된다. 다음의 경우에는 prepend 값이 출력되지 않고 무시된다. (a) 요소의 결과 내용이 공백인 경우, (b) 요소가 처음으로 내용을 만들어 냈고 removeFirstPrepend 속성이 true 요소에 내포되어 있는 경우, (c) 요소 다음에 공백이 아닌 내용을 처음으로 출력하고 prepend 값이 지정돼 있는 요소일 경우

Open(선택적)

Open 값은 요소 내용에 접두사로 붙이기 위해 사용한다. Open 값은 요소의 결과 내용이 공백일 때는 접두사로 붙지 않는다. Open 값은 prepend 속성 값보다는 뒤에 붙는다. 예를 들어 prepend=”OR” 이고 open=” (“ 라면 조합된 결과는 “OR (“ 것이다.

Close(선택적)

Close 값은 요소 결과 내용의 뒤에 덧붙이는데 사용한다. 값은 요소 내용이 공백이라면 덧붙여지지 않을 것이다.

removeFirstPrepend(선택적)

값으로 처음으로 내용을 출력하는 자식 요소의 prepend 속성 값을 출력할지 여부를 결정한다.

모든 속성은 아래에 있는 모든 요소에서 사용 가능하다.

: 파라미터 객체가 존재하는지 평가

: 파라미터가 존재하지 않는지 평가

다음은 select 구문에서 파라미터 요소를 사용하는 방법을 보여준다.

예제에서 다시 간단한 select 구문을 생성하고, 이번에는 productType 파라미터를 기반으로 해서 결과 값을 걸러내는 WHERE 절을 선택적으로 생성해준다.

8.2.5 요소

요소는 컬렉션이나 배열로 프로퍼티를 받아서, 값들로부터 SQL 반복적인 부분을 생성한다. 리스트의 값들을 conjunction 속성에 지정된 값을 문자열 사이 사이에 넣는 방식으로 분리하이 SQL 구문의 일부가 되도록 출력한다. Open 속성 값은 출력된 리스트의 부분에 붙어서 나온다. Close 속성의 값은 출력된 리스트의 뒷부분에 붙어서 나온다. 표에서 요소의 속성을 있다.

Property(필수)

리스트(컬렉션 혹은 배열) 포함하고 있는 파라미터의 프로퍼티

Prepend(선택적)

값은 요소 내용의 가장 앞에 출력된다. 다음의 경우에는 prepend 값이 출력되지 않고 무시된다. (a) 요소의 결과 내용이 공백인 경우, (b) 요소가 처음으로 내용을 만들어 냈고 removeFirstPrepend 속성이 true 요소에 내포되어 있는 경우, (c) 요소 다음에 공백이 아닌 내용을 처음으로 출력하고 prepend 값이 지정돼 있는 요소일 경우.

Open(선택적)

Open 값은 요소 내용에 접두사로 붙이기 위해 사용한다. Open 값은 요소의 결과 내용이 공백일 때는 접두사로 붙지 않는다. Open 값은 prepend 속성값보다는 뒤에 붙는다. 예를 들어 prepend=”OR” 이고 open=”(“라면 조합된 결과는 “OR (“ 것이다.

Close(선택적)

Close 값은 요소 결과 내용의 뒤에 덧붙이는데 사용한다. 값은 요소 내용이 공백이라면 덧붙여지지 않을 것이다.

Conjunction(선택적)

속성의 값은 목록의 사이 사이에 출력되어 SQL 문장을 구성한다.

removeFirstPrepend(선택적)

값으로 처음으로 내용을 출력하는 자식 요소의 prepend 속성 값을 출력할지 여부를 결정한다.

아래는 요소를 이용해서 SQL 구문에서 복잡한 WHERE 조건절을 구성하는 방법을 보여준다.

예제에서는 select 문을 만들고, 다음에 제품 타입(product type) 목록을 반복적으로 돌아서, 제품 타입에 따라 결과를 필터링하는 복잡한 조건절을 생성한다.

1.3. 모두 적용한 간단한 예제

동적 SQL 기본 지식을 모두 배웠다. 간단한 검색 기능을 구현하여 애플리케이션에서 사용해보자. 예제에서는 JGameStore 라는 애플리케이션을 사용할 것이다. 애플리케이션은 14장에서 정식으로 소개할 예정이다. 애플리케이션을 구축하면서 동적 SQL 개념을 이해하고 조합해서 사용하는데 도움이 있도록, 간결한 접근 방법을 적용할 것이다.

예제를 보기전에, 우리가 사용할 프로세스를 살펴보자. 프로세스 자체는 상당히 간단하다. 그리고 애플리케이션이 성숙해감에 따라 처리 과정들은 서로 다른 순서와 다양한 수준의 노력을 요할 있다. 초반에 해줘야 하는 작업이 약간 있다. 먼저 기반을 다져놔야 하기 때문이다. 일단 기반을 다지고 나면 위에 무언가를 구축하는 것은 별로 복잡하지 않다.

프로세스는 가지 기본적인 단계로 구성된다.

1. 데이터를 가져오고 표시하는 방법을 지정한다.

2. 어떤 데이터베이스 구조를 사용할 결정한다.

3. 정적인 형태로 SQL 작성한다.

4. 정적인 SQL 동적 SQL 요소를 적용한다.

상당히 간단한 페이지이지만, 모두 알다시피 항상 눈에 보이는 것보다 많은 일이 뒤에서 이뤄지게 마련이다. 다음 절에서 위의 작업을 수행하는 코드를 살펴볼 것이다.

8.3.1 데이터를 가져오고 표시하는 방법을 정의하기

JGameStore 애플리케이션의 페이지에 간단한 검색 필드와 검색 버튼을 구현할 것이다. 검색 필드는 공백으로 단어를 구분한다. 예를 들어 ‘Adventure Deus’ 입력했다면 ‘Adventure’ ‘Deus’ 라는 개의 단어로 분리될 것이다. 각각의 단어를 제품의 카테고리 변호 (CategoryId), 제품명(name), 그리고 설명(description) 내에서 찾을 있는지 검사할 것이다. 일단 검색 버튼을 클릭하면 결과로 나온 모든 제품들을 페이지당 개씩 출력한다.

8.3.2 데이터베이스 구조 결정하기

테이블 구조를 어떻게 정의할 알아보자. 우리는 categoryId, name, 그리고 description 대해 검색을 한다. 따라서 Product 테이블이 이러한 요구사항을 충족시킬 있는 정도가 되게끔만 만들자. Product 테이블은 검색하고 출력할 필요가 있는 모든 제품 정보를 포함하고 있다.

8.3.3 정적인 형태로 SQL 작성하기

처음 시작할 때는 검색 결과에서 표시해줄 모든 필드를 가져오는 정적인 SQL 만든다. 다음에서 제품 정보를 출력하는데 사용할 정적인 쿼리를 있다.

SELECT

PRODUCTID,

NAME,

DESCRIPTION,

IMAGE,

CATEGORYID

FROM PRODUCT

WHERE

lower(name) like 'adventure%' OR

lower(categoryid) like 'adventure%' OR

lower(description) like 'adventure%' OR

lower(name) like 'Deus%' OR

lower(categoryid) like 'deus%' OR

lower(description) like 'deus%'

이제 입력과 출력 그리고 데이터 베이스 테이블을 모두 갖추었다. SQL 구문의 모형을 만들때가 것이다. 앞서 언급한 정보를 가지고 있다면 SQL 생성하는 것은 지극히 간단하다. 우리에게 필요한 제품 목록은 SELECT 구문을 통해 받을 있다. Name, categoryId 그리고 description 제공받은 단어를 비교하는 검색 조건은 WHERE 절에 나온다.

8.3.4 동적인 SQL 요소를 정적인 SQL 적용하기

모의 SQL 구문을 살펴보면 SQL 구문을 상세히 분석하고 어디에서 동적인 요소를 사용할지 결정할 있다. SELECT 절은 정적이기 때문에 여기에선 아무런 동적인 요소도 필요 없다. WHERE 절은 동적으로 변경해줄 필요가 있다.

동적 요소들로 매핑 구문에 전달받은 파라미터를 활용할 있다. 따라서 어떤 파라미터들이 있고, 파라미터들에 동적 요소를 어떻게 적용할 생각해 필요가 있다. 매핑 구문이 받는 파라미터는 String(java.lang.String) 리스트이다. 간단한 리스트를 직접 사용하기 때문에 요소를 사용할 것이다. 요소는 문자열의 리스트를 반복적으로 돌면서 categoryId, name, description 검색하는 조건을 생성한다. 값을 OR 지정한 conjunction 속성을 요소에 추가해야 함을 잊어서는 된다. Conjunction 속성은 단어 검색 조건을 함께 연결한다. 여기서 알아둬야 점이 있는데, 매핑 구문의 이름은 매핑 구문에 포함된 SQL 수행하는 기능을 제대로 설명해 있게 정해야 한다는 것이다. 경우에는 매핑 구문의 이름을 searchProductList 라고 정했다. 매핑 구문의 이름을 읽었을 구문의 목적을 즉시 명확하게 파악할 있어야 한다.

sqlMap.queryForPaginatedList(

“searchProductList”, parameterObject, PAGE_SIZE);

마지막 요구 사항으로 번에 개의 레코드만을 반환해야 한다. 이를 위해 pageSize 파라미터를 받는 queryForPaginatedList() 메서드를 사용해서 매핑 구문을 호출하여 요구 사항을 처리할 것이다. 이렇게 하면 반환되는 레코드의 개수를 제어할 있다.

간단한 예제를 전체적으로 둘러보면서 동적 SQL 작성 계획을 세우고 개발하는 방법을 공부해 보았다. 동적 SQL 여러가지 선택사항이 필요한 가지 목적에만 사용해야 한다는 것을 기억하라. 여러 가지 목적을 달성하기 위해 동적 SQL 사용해서는 된다. 예제에서 동적 SQL 목적은 사용자가 어떻게 제품을 선택할지를 처리하는 것이다. 명심해야할 것은 동적 SQL 복잡한 것을 간단하게 만들려고 나온 것이지, 간단한 것을 복잡하게 만들려고 나온 것이 아니라는 점이다. 다음으로 동적 SQL 상세한 사용법을 살펴보자.

1.4. 고급 동적 SQL 기법

8.4.1 결과 데이터 정의하기

일반적인 말로 우리가 출력하고자 하는 것을 정의해보자. 우리의 장바구니 애플리케이션은 사용자가 지정한 검색 조건에 따라 출력할 제품 목록을 필요로 한다. 출력은 categories, products, 그리고 manufacturers 포함한 선택 조건에 기초하여 결정될 것이다. 제품은 페이지당 4개씩 표시되어야만 한다. 동적 SQL 페이지 처리된 제품 목록을 생성하고 사용자가 입력하는 복잡하고 다양한 값을 처리할 있어야만 한다.

그렇다면 검색 페이지는 데이터베이스와 어떤 식으로 작동할까? 아래에서 물음에 대한 답변의 일부를 있다.

public class ProductSearchCriteria {

private String[] categoryIds;

private String productName;

private String productDescription;

private String itemName;

private String itemDescription;

// setters and getters

}

본래, 검색 페이지의 필드는 간단한 자바빈즈인 검색 기준 클래스의 프로퍼티에 매핑된다.

8.4.2 정적인 형태로 SQL 작성하기

이제 입력과 출력을 모두 정의했으니, 데이터베이스 쿼리 툴에서 실행할 있는 간단한 정적 SQL 사용하여 SQL 작성할 있다. 다음에서 개발의 시작점이 되면서 동적 SQL 구성하는 기초가 정적 SQL 구문을 있다.

SELECT

p.PRODUCTID AS PRODUCTID,

p.NAME AS NAME,

p.DESCRIPTION AS DESCRIPTION,

p.IMAGE AS IMAGE,

p.CATEGORYID AS CATEGORYID

FROM Product p

INNER JOIN Category c ON

c.categoryId=p.categoryId

INNER JOIN Item i ON

i.productId = p.productId

WHERE

c.categoryId IN ('ACTADV')

AND

p.name LIKE '007'

AND

p.description LIKE '007'

AND

i.name LIKE 'PS2'

AND

i.description LIKE 'PS2'

우리는 이제 필요한 입력이 무엇이고, 어떤 테이블이 필요한지 알고 있으므로 모의로 정적 SQL 쿼리를 만들어 있다. 우리의 정적 SQL 예제에서 WHERE 조건절이 조합 가능한 모든 조건들을 모두 포함하여 작성되었음을 있다. 무엇을 동적으로 만들어야 하는지 알려면 정적 SQL 모든 사항을 고려해서 말들어 보는 것이 중요하다. 동적 SQL 조합하는 부분을 보자.

8.4.3 동적 SQL 요소를 정적 SQL 적용하기

모의로 정적 SQL 모두 만들었으니, 이제 동적 요소들을 적용해보자. 다듬에서 있듯이 동적 SQL 조금 복잡해진다. 가장 상위 레벨의 요소로 사용했음을 주의해서 보라. 어떠한 prepend, open 이나 close 값도 필요하지 않기 때문에 부모 요소로 사용할 필요는 없다. 단순히 내용을 표시하거나 표시하지 않거나 것이다. 요소는 LEFT JOIN 포함할지 여부를 결정한다.

시작 select 요소 (1)에서 파라미터 클래스 타입은 productSearch, 그리고 결과 클래스 타입은 product라는 별칭을 사용하여 정의하였다. SQL 부분 (2) 매핑 구문에서 생성할 있는 가장 작은 단위의 SQL이다. 예를 들면 사용자가 모든 것을 반환하도록 검색하고자 하는 경우에 SQL 사용한다. 요소 (3) 조인이 필요한지를 결정하기 위해 부분 밖에서 사용되었다. 우리는 “WHERE”라는 prepend 값을 가지는 시작 dynamic 요소 (4) 사용한다. 결과가 되는 내용물이 없다면 prepend 속성에 명시된 “WHERE” 값은 무시될 것이다. 다시 기억을 상기시켜보자면, dynamic 요소는 요소안의 내용에서 가장 처음으로 내용을 만들어내는 요소의 prepend 값을 무조건 제거한다. 요소(5) 주의해서 봐야한다. 이는 요소 (4) 짝을 이루어서 작동하여 특정 카테고리에 관한 검색 조건을 구성하는데 필요한 SQL 컴포넌트를 만들어 낸다. iBATIS 관습적인 문제 때문에, prepend 속성이 필요하다. Prepend 속성이 없는 첫번째 자식 요소에서는 내용이 생성되더라도 prepend 제거되었다고 간주하지 않기 때문에, 다음 번에 나온 prepend 요소가 무시되게 되는 현상이 일어난다. 해당 요소에 prepend 속성이 필요 없다 하더라도, 규칙에 따라 prepend 속성을 항상 명시해 주도록 한다. 요소는 productName, productDescription, itemName 그리고 itemDescription 프로퍼티가 ‘ ‘(빈값) 혹은 NULL인지 여부를 결정하는 평가를 수행한다.

그렇다면 괴물 같은 구문을 어떻게 호출할까?

queryForPaginatedList (

“product.searchProductsWithProductSearch”,

productSearch, PAGE_SIZE);

앞의 예제에서 우리의 마지막 요구사항은 번에 개의 레코드만을 반환해야 한다는 것이었다. 이를 위해 페이지 크기 파라미터를 받는 queryForPaginatedList 메서드를 사용하여 구문을 호출한다. 범위를 지정하는 queryForPaginatedList 사용하는 방법에 대해서는 8.3.4절의 예제를 보라.

복잡한 예제를 보며 다소 복잡한 동적 SQL 어떻게 수행하는지 공부하였다. 여기서는 동적 SQL 사용자가 입력한 검색 기준에 따라 제품 목록을 제공한다는 하나의 목적만을 수행하도록 하였다. 비록 예제가 조금 복잡해 보이긴 하지만, 모든 것을 자바 코드로만 작성하는 것이 훨씬 어렵다는 것은 명백하다. 이제 당신 자신의 동적 SQL 구성할수 있는 준비가 되었으리라 믿는다.

iBATIS 동적 SQL 동적 SQL 처리하는 다른 솔루션들과 비교하는 방법을 이해하는것도 중요하다. 동적 SQL 수행하는 몇몇 다른 방법들을 간단히 알아보자.

1.5. 동적 SQL 대안이 되는 접근법

동적 SQL 결코 새로운 개념이 아니다. 조건에 따라 생성되는 SQL 쿼리는 항상 발생하는 복잡한 요구사항이다. 과거에는 동적 SQL 효율적으로