Part 15. 검색 처리

15.3 검색 조건 처리를 위한 Criteria의 변화

  • 페이징 처리에 사용했던 Criteria의 의도는 단순히 'pageNum'과 'amount'라는 파라미터를 수집하기 위해서다.
  • 페이징 처리에 검색 조건 처리가 들어가면 Criteria 역시 변화가 필요하다.
  • 검색 조건을 처리하기 위해서는 검색 조건(type)과 검색에 사용하는 키워드가 필요하므로 기존의 Criteria를 확장할 필요가 있다.
  • 확장 방법으로는 상속 방법을 이용하거나 직접 Criteria 클래스를 수정하는 방식을 생각해 볼 수 있는데, 예제에서는 직접 Criteria 클래스를 수정한다.
< Criteria 클래스 수정 >
package org.zerock.domain;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class Criteria {
	private int pageNum;
	private int amount;
	private String type;
	private String keyword;
	public Criteria() {
		this(1,10);
	}
	public Criteria(int pageNum, int amount) {
		this.pageNum = pageNum;
		this.amount = amount;
	}
	public String[] getTypeArr() {
		return type == null? new String[] {}: type.split("");
	}
}
  • Criteria 클래스는 type과 keyword라는 변수를 추가한다.
  • getter/setter는 Lombok을 통해 생성하고, getTypeArr은 검색 조건이 각 글자(T, W, C)로 구성되어 있으므로 검색 조건을 배열로 만들어 한 번에 처리하기 위함이다.
  • getTypeArr()을 이용해 MyBatis의 동적 태그를 활용할 수 있다.

15.3.1 BoardMapper.xml에서 Criteria 처리

  • BoardMapper.xml은 기존의 getListWithPaging()을 수정해 동적 SQL을 처리한다.
< BoardMapper.xml의 검색 및 페이징 처리 >
<select id="getListWithPaging" resultType="org.zerock.domain.BoardVO">
	<![CDATA[
		select 
			bno, title, content, writer, regdate, updatedate
		from
			(
			select /*+INDEX_DESC(tbl_board pk_board) */
				rownum rn, bno, title, content, writer, regdate, updatedate
			from
				tbl_board
			where
	]]>
		<trim prefix="(" suffix=") AND " prefixOverrides="OR">
			<foreach item='type' collection="typeArr">
				<trim prefix="OR">
					<choose>
						<when test="type == 'T'.toString()">
							title like '%'||#{keyword}||'%'
						</when>
						<when test="type == 'C'.toString()">
							content like '%'||#{keyword}||'%'
						</when>
						<when test="type == 'W'.toString()">
							writer like '%'||#{keyword}||'%'
						</when>
					</choose>
				</trim>
			</foreach>
		</trim>
	 <![CDATA[
		 	rownum <= #{pageNum} * #{amount}
		 	)
		where rn > (#{pageNum} -1) * #{amount}
	 ]]>
</select>
  • 검색 조건이 3가지이므로 총 6가지의 조합이 가능하지만, 각 문자열을 이용해 검색 조건을 결합하는 형태로 하면 3개의 동적 SQL 구문만으로 처리를 할 수 있다.
  • < foreach >를 이용해 검색 조건들을 처리하는데 typeArr이라는 속성을 이용한다.
  • MyBatis는 원하는 속성을 찾을 때 getTypeArr()과 같이 이름에 기반을 두어 검색하기 때문에 Criteria에서 만들어둔 getTypeArr() 결과인 문자열의 배열이 < foreach >의 대상이 된다(MyBatis는 엄격하게 Java Beans의 규칙을 따르지 않고, get/set 메서드만을 활용하는 방식이다.).
  • < choose > 안쪽의 동적 SQL은 'OR title... OR content... OR writer...' 와 같은 구문을 만들어내게 된다.
  • 따라서 바깥쪽에서는 < trim > 을 이용해 맨 앞에서 생성되는 'OR'을 없애준다.
  • 위의 동적 SQL은 상황에 따라 다음과 같은 SQL을 생성한다.
  • 동적 SQL은 경우에 따라 여러 종류의 SQL이 생성될 수 있으므로 제대로 동작하는지 반드시 여러 번의 확인을 거쳐야만 한다.
  • 기존에 BoardMapperTests를 만들어 두었으니 이를 이용해 테스트 코드를 작성한다.
< src/test/java 밑의 BoardMapperTests 클래스 >
@Test
	  public void testSearch() {
		Criteria cri = new Criteria();
	    cri.setKeyword("새로");
	    cri.setType("TC");
	    List<BoardVO> list = mapper.getListWithPaging(cri);
		list.forEach(board -> log.info(board));
	  }
  • testSearch()는 Criteria 객체의 input과 keyword를 넣어 원하는 SQL이 생성되는지 확인하기 위함이다.
  • 중요한 것은 실행 결과가 아니라 실행할 때 만들어지는 SQL이다.
  • 아래와 같이 각 상황에 맞게 SQL이 올바르게 만들어지는지 확인해야 한다.

< sql > < include >와 검색 데이터의 개수 처리

  • 동적 SQL을 이용해 검색 조건을 처리하는 부분은 해당 데이터의 개수를 처리하는 부분에서도 동일하게 적용되어야만 한다.
  • 이 경우 가장 간단한 방법은 동적 SQL을 처리하는 부분을 그대로 복사해서 넣어줄 수 있지만, 만일 동적 SQL을 수정하는 경우에는 매번 목록을 가져오는 SQL과 데이터 개수를 처리하는 SQL쪽을 같이 수정해야 한다.
  • MyBatis는 < sql >이라는 태그를 이용해 SQL의 일부를 별도로 보관하고, 필요한 경우에 include시키는 형태로 사용할 수 있다.
< BoardMapper.xml의 목록과 데이터 개수 처리 >
<!-- 목록과 데이터 개수 처리 -->
<sql id="criteria">
	<trim prefix="(" suffix= ") AND " prefixOverrides="OR">
		<foreach item='type' collection="typeArr">
			<trim prefix="OR">
				<choose>
					<when test="type == 'T'.toString()">
						title like '%'||#{keyword}||'%'
					</when>
					<when test="type == 'C'.toString()">
						content like '%'||#{keyword}||'%'
					</when>
					<when test="type == 'W'.toString()">
						writer like '%'||#{keyword}||'%'
					</when>	
				</choose>
			</trim>
		</foreach>
	</trim>
</sql>
<!-- getListWithpaging 태그 추가 -->
<select id="getListWithPaging" resultType="org.zerock.domain.BoardVO">
	<![CDATA[
		select 
			bno, title, content, writer, regdate, updatedate
		from
			(
			select /*+INDEX_DESC(tbl_board pk_board) */
				rownum rn, bno, title, content, writer, regdate, updatedate
			from
				tbl_board
			where
	]]>		
		<include refid="criteria"></include>		 	
	 <![CDATA[
		 	rownum <= #{pageNum} * #{amount}
		 	)
		where rn > (#{pageNum} -1) * #{amount}
	 ]]>
</select>
<!-- 전체 데이터 수 가져오기 -->
<select id="getTotalCount" resultType="int">
	select count(*) from tbl_board 
	where 
<include refid="criteria"></include>

bno > 0
```
  • < sql > 태그는 id라는 속성을 이용해 필요한 경우에 동일한 SQL의 일부를 재사용할 수 있게 한다.
profile
한 걸음 한 걸음 나아가는 개발자

0개의 댓글