웹 페이지에서 흔하게 볼 수 있는 형식 중 하나인 필터 기능을 TDD 방식으로 구현해보자.
체크되는 필터에 해당하는 내용만 게시판 글로 볼 수 있는 기능이다.
1. 기존 DB에 TYPE 컬럼 추가
ALTER TABLE BOARD ADD TYPE VARCHAR2(2) DEFAULT 'B1';
-- 위 쿼리문을 지우는 쿼리문
ALTER TABLE BOARD DROP COLUMN TYPE;
- 데이터 베이스에 BOARD 테이블이 이미 생성되어 있는 경우, 필터에 사용할 컬럼을 추가하자.
- 데이터 베이스 준비(구분할 수 있도록 적정량의 TYPE 컬럼 값을 B1, B2, B3, ...여러 개 준비)
체크박스를 사용한다고 가정, 체크 박스 필터에 걸리는 내용만 조회하도록 한다.
이 때, <checkbox name="XXX""> 같은 name 속성을 갖지만, 체크된 각각의 value는 다르다.
체크박스에서 서버로 실제 파라미터를 넘길 때는 TYPE 컬럼을 사용할 것이다.
하나일 경우도 있지만, 2개 이상의 TYPE이 체크될 수도 있다.
cf. 서블릿의 경우,
String[] XXX = request.getParameter("XXX"); 혹은 String[] XXX = request.getParameterValues("XXX");로 가져올 수 있고, 넘어오는 값은 {"B1", "B2", "B3", ...} 혹은 {"B1"} 이런 식이 될 것이다.
2. BoardServiceTest.java에 @Test 생성
@Test
public void findAllTest() {
String[] filters = new String[] {"B2", "B3"};
// BY request.getParameterValues("filter");
// 체크 박스의 name 속성을 filter로 바꿨다.
// 실제 서버에서 체크 박스의 value 값을 꺼내온다.
// filter라는 이름의 배열로 가져와서
// -> 리스트 객체 변환 -> 서비스에 넘기게 될 것이다.
List<Board> list = null;
// Board 형식의 List를 얻어오자.
list = service.findAll(filters);
// service의 findAll 메소드의 매개 값으로 filters 배열을 넘겨줌으로써
// list의 전체 조회 결과는 filter를 만족하는 결과만 나온다.
assertThat(list).isNotNull();
// list의 결과는 null이 아닐 것이다.
}
3. BoardService.java에 filters 배열을 받는 findAll() 메소드 생성
public List<Board> findAll(String[] filters) {
List<Board> list = null;
SqlSession session = getSession();
list = dao.findAll(session, filters);
session.close();
return list;
}
- 기존에 findAll() 메소드가 있어도 매개값이 다르기 때문에 가능하다 -> 오버로딩
4. BoardDao.java에 session과 filters 배열을 받는 findAll() 메소드 생성
public List<Board> findAll(SqlSession session, String[] filters) {
Map<String, Object> map = new HashMap<>();
map.put("filters", filters);
return session.selectList("boardMapper.selectBoardListByFilters", map);
}
※※※※※ 아래 주의 사항을 읽고 와서 해결해야 하는 부분(미리 해놨으면 주의 사항에 해당되지 않을 부분) ※※※※※
- 기존에는 변수명으로 접근했지만,
list나 배열 타입은 파라미터로 전달하게 되면 내부적으로는 map 객체로 변환이 된다.
key | value | |
리스트 list | collection 혹은 list | obejct |
배열 array | array | object |
이런식으로 바뀐다.
- mapper의 쿼리문에서는 list 타입은 list로, 배열은 array 라는 이름으로 사용할 수 있는 것이다.
- 내부적으로 map 형태로 변환되기 때문에 파라미터로 문자열이나, 객체를 넘길 때에도
key 값은 변수명, value는 해당하는 객체가 된다.
- 그래도 굳이 굳이 filters라는 이름을 그대로 쓰고 싶다면 내부적으로 map 타입으로 변환되지 않도록
아예 map객체로 만들어주면 된다.
/*
* List 타입이나 Array 타입의 데이터를 실제 쿼리문 수행시킬 때 파라미터로 전달하면 내부적으로는 Map으로 타입이 변환되어서 저장되기 때문에
* Mapper에서는 list나 array라는 이름으로 사용해야 한다.
*
* Dao.java
* session.selectList("boardMapper.selectBoardListByFilters", filters);
*
* Mapper.xml
* <if test="array != null">
* ...
* </if>
*
* 만약에, filters 라는 이름을 Mapper에서 사용하고 싶다면 Map map 객체를 생성하고 -> map에 담아서 파라미터로 전달한다.
* Mapper에서는 key 값인 "filters"를 가지고 배열 객체인 filters에 접근할 수 있게 되는 것이다.
*/
5. Board.java 클래스에 type 필드 추가
private String type;
- 이제 쿼리문을 가지고 값을 가져올 것인데, 하나의 행을 Board 타입의 객체에 매핑해 주기 때문에 같은 이름의 필드가 필요하다.
6. board-mapper.xml에 쿼리문 작성하기
<!-- 기본 쿼리 -->
<sql id="boardListSql">
SELECT B.NO,
B.TITLE,
M.ID,
B.READCOUNT,
B.ORIGINAL_FILENAME,
B.RENAMED_FILENAME,
B.CONTENT,
B.TYPE,
B.CREATE_DATE,
B.MODIFY_DATE
FROM BOARD B
JOIN MEMBER M ON(B.WRITER_NO = M.NO)
WHERE B.STATUS = 'Y'
</sql>
<select id="selectBoardListByFilters" parameterType="map" resultMap="boardListResultMap">
<include refid="boardListSql" />
<if test="filters != null">
<!--
AND B.TYPE IN ('B2', 'B3')
위 결과를 만들기 위해서 foreach 태그를 사용한다.
- collection 속성 : 파라미터로 넘어온 배열이나 list를 지정한다.
- item : 배열이나 list의 각 요소들의 값이 들어가는 변수이다. (속성명은 임의로 작성)
- index : 반복 횟수(0 부터 시작 한다.)
- open : foreach 반복 시작 전에 출력할 문자열을 지정한다.
- close : 반복 종료 전에 출력할 문자열을 지정한다.
- separator : 반복할 때마다 반복을 구분할 구분자를 지정한다.
-->
AND B.TYPE IN
<foreach collection="filters" item="filter" open="(" separator="," close=")" >
#{filter}
</foreach>
</if>
</select>
- 자바 클래스의 필드명과, 오라클 DB의 컬럼명이 100% 동일하지 않기 때문에, 명시적 매핑을 해주는 resultMap을 사용할 것이다.
- 사전에 미리 board-mapper.xml 파일에 <resultMap> 으로 만들어 두었다.
- 조회된 결과를 resultMap에 있는 매핑 정보를 보고 매핑해 줄 것이다.
- 이 때, 사전에 만들어 두었던 <resultMap> 안에 방금 추가한 TYPE 컬럼에 대한 명시가 없어도, 자동 매핑으로 값이 들어가게 된다. 그래도 명시적으로 추가해주는게 좋기 때문에 type에 대한 것도 추가 기입한다.
- filters 배열의 결과에 null이 있을 수도 있기 때문에 그것에 대한 처리를 한다.
<if test="filters != null"></if> 부분
null이 아니면 AND B.TYPE IN ('B2', 'B3') 쿼리가 포함될 것이다.
※※※※※※ 여기에서 주의 사항 ※※※※※
임의로 지정했던 String[] filters는 사실 이런 오류를 발생시킨다.
org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause : org.apache.ibatis.binding.BindingException:
Parameter 'filters' not found. Available parameters are [array]
- 이것은 파라미터의 이름으로 'filters' 라는 것을 찾을 수가 없어서 그런 것이다.
- 따라서, <if test="filters != null"></if>를 <if test="array != null"></if>로 바꾼다.
'Programming > Framework' 카테고리의 다른 글
DAY 141. TDD 방식으로 게시글 상세조회 기능(댓글 포함) (0) | 2022.01.06 |
---|---|
DAY 140. TDD 방식으로 게시판 페이징 기능 (0) | 2022.01.05 |
DAY 138. Mybatis - 동적 SQL (0) | 2022.01.03 |
DAY 137. Mybatis - xml 파일 만들기 (0) | 2022.01.02 |
DAY 136. Mybatis 개념 & 동작 구조 (0) | 2022.01.01 |