본문 바로가기
교재 실습/자바 웹 개발 워크북

118. 동적 SQL의 사용 (5)

by Jint 2022. 9. 22.

5. <set> 엘리먼트의 활용

프로젝트 정보를 변경하는 기능에 대해서도 동적 SQL을 적용해본다. 지금까지는 한 개의 항목을 바꾸더라도 전체 항목을 변경해야 했다. 다음은 프로젝트 정보를 변경하는 UPDATE문이다. 현재는 프로젝트 정보를 변경할 때 이 SQL문을 실행하고 있다.

UPDATE PROJECTS
SET PNAME = #{title}
    ,CONTENT = #{content}
    ,STA_DATE = #{startDate}
    ,END_DATE = #{endDate}
    ,STATE = #{state}
    ,TAGS = #{tags}
WHERE PNO = #{no}

이 UPDATE문의 문제는 프로젝트 제목(title)만 변경하고 싶어도 내용, 시작일, 종료일, 상태, 태그까지 변경해야 한다는 것이다. 이 문제를 해결하기 위해 변경하는 상황을 모두 고려하여 UPDATE문을 만든다면 그 경우의 수가 너무 많다. 한 개의 컬럼만 변경하는 경우(6가지)에서 모든 컬럼을 변경하는 경우(1가지)까지 계산해 보면 총 63가지가 나온다. 즉 63개의 UPDATE문을 만들어야 한다. 이것은 오히려 개발을 더욱 복잡하게 만든다.

다음은 컬럼을 변경하는 경우의 수를 구하는 공식이다. 수학에서 나오는 조합(Combination)을 이용하였다(그림 1).

그림 1 (n개의 컬럼을 변경하는 경우의 수)

바로 이런 상황일 때 동적 SQL이 필요한 것이다. mybatis의 동적 SQL을 이용하면 63개의 SQL문을 만드는 대신 단 한 개의 SQL문만으로 모두 처리할 수 있다. 다음의 지시에 따라 UPDATE문을 변경한다.

spms.dao 패키지의 MySqlProjectDao.xml 파일을 열고, 프로젝트를 변경하는 SQL문을 찾아 다음과 같이 편집한다.

<update id="update" parameterType="project">
    UPDATE PROJECTS
    <set>
        <if test="title != null">PNAME = #{title},</if>
        <if test="content != null">CONTENT = #{content},</if>
        <if test="startDate != null">STA_DATE = #{startDate},</if>
        <if test="endDate != null">END_DATE = #{endDate},</if>
        <if test="state != null">STATE = #{state},</if>
        <if test="tags != null">TAGS = #{tags}</if>
    </set>
    WHERE PNO = #{no}
</update>

parameterType 속성을 "project"에서 "map"으로 바꾸었다. 앞으로는 변경할 값을 Project 객체가 아닌 Map 객체에 담아야 한다. 그리고 SET절 자리에 <set> 태그를 작성하였다.

 

- <set> 엘리먼트

<set> 태그는 SET절을 만든다. test의 값이 참이면 <if> 태그의 콘텐츠를 반환한다. 예를 들어 title과 startDate의 값이 null이 아니면 다음의 SET절이 만들어 진다.

SET PNAME = '변경한제목', STA_DATE = '변경한날짜'

mybatis는 SET절의 끝에 콤마(,)가 있으면 제거한다. 따라서 STA_DATE 항목에 붙은 콤마(,)가 제거된다.

 

6. MySqlProjectDao 클래스 변경

UPDATE문이 변경되었기 때문에 이에 맞추어 DAO 클래스도 변경해야 한다.

spms.dao 패키지에서 MySqlProjectDao 클래스를 열고 다음과 같이 update() 메서드를 변경한다.

/**
 * 프로젝트 수정
 * @param project
 * @return count
 */
public int update(Project project) throws Exception { 
    SqlSession sqlSession = sqlSessionFactory.openSession();
    try {
        Project original = sqlSession.selectOne("spms.dao.ProjectDao.selectOne", project.getNo());
        Hashtable<String,Object> paramMap = new Hashtable<String,Object>();
        if(!project.getTitle().equals(original.getTitle())) {
            paramMap.put("title", project.getTitle());
        }
        if(!project.getContent().equals(original.getContent())) {
            paramMap.put("content", project.getContent());
        }
        if(project.getStartDate().compareTo(original.getStartDate()) != 0) {
            paramMap.put("startDate", project.getStartDate());
        }
        if(project.getEndDate().compareTo(original.getEndDate()) != 0) {
            paramMap.put("endDate", project.getEndDate());
        }
        if(project.getState() != original.getState()) {
            paramMap.put("state", project.getState());
        }
        if(!project.getTags().equals(original.getTags())) {
            paramMap.put("tags", project.getTags());
        }

        if(paramMap.size() > 0) {
            paramMap.put("no", project.getNo());
            //update() : 두 번째 매개변수로 프로젝트 정보 담은 값 객체 전달
            //int count = sqlSession.update("spms.dao.ProjectDao.update", project);
            int count = sqlSession.update("spms.dao.ProjectDao.update", paramMap);
            //임시 데이터베이스에 보관된 작업 결과를 운영 데이터베이스에 적용
            sqlSession.commit();
            return count;
        }else {
            return 0;
        }
    } finally {
        sqlSession.close();
    }
}

먼저 원래의 프로젝트 정보를 가져온다. 이 값과 사용자가 입력한 값을 비교할 것이다.

Project original = sqlSession.selectOne("spms.dao.ProjectDao.selectOne", project.getNo());

UPDATE문에 전달할 Map 객체를 준비한다. 원래의 값과 사용자가 입력한 값을 비교하여 값이 바뀌었다면 Map 객체에 저장한다.

Hashtable<String,Object> paramMap = new Hashtable<String,Object>();
if(!project.getTitle().equals(original.getTitle())) {
    paramMap.put("title", project.getTitle());
}

Map 객체에 저장된 값이 있다면, 즉 변경된 값이 있다면 UPDATE문을 실행한다. 없다면 0을 반환한다.

if(paramMap.size() > 0) {
    paramMap.put("no", project.getNo());
    //update() : 두 번째 매개변수로 프로젝트 정보 담은 값 객체 전달
    //int count = sqlSession.update("spms.dao.ProjectDao.update", project);
    int count = sqlSession.update("spms.dao.ProjectDao.update", paramMap);
    //임시 데이터베이스에 보관된 작업 결과를 운영 데이터베이스에 적용
    sqlSession.commit();
    return count;
}else {
    return 0;
}

 

7. 동적으로 생성된 UPDATE문 확인

톰캣 서버를 재시작한다. 웹 브라우저에서 http://localhost:9999/web07/project/list.do를 요청한다. 그리고 프로젝트 목록에서 변경할 항목을 클릭한다(그림 2).

그림 2 (프로젝트 목록)

프로젝트 정보 페이지에서 제목과 시작일을 변경하고, <저장> 버튼을 클릭한다(그림 3, 그림 4).

 

그림 3 (프로젝트 상세 정보에서 정보 변경)
그림 4 (변경 결과)

이클립스 콘솔창에 출력된 내용을 확인해본다. 다음은 UPDATE문을 실행할 때 출력된 로그이다(그림 5).

 

그림 5 (이클립스 콘솔창에 출력된 로그의 일부분)

동적 SQL로 만든 UPDATE문을 보면 SET절의 항목이 PNAME과 STA_DATE이다.

UPDATE PROJECTS SET PNAME = ?, STA_DATE = ? WHERE PNO = ?

즉 사용자가 변경한 제목과 시작일만 SET절에 추가되었다. 또한, 로그 내용에는 각 입력 매개변수에 들어갈 값도 포함하고 있다. 사용자가 변경한 제목과 시작일의 값이 보인다.

Parameters: hello!(String), 2022-02-15(Date), 4(Integer)

 

이렇게 mybatis의 동적 SQL 기능을 이용하면 하나의 SQL문으로 다양한 상황을 처리할 수 있어 매우 편리하다. 그리고 로그 출력 기능을 켜 놓으면 내부에서 진행되는 상황을 살펴볼 수 있어 개발에 많은 도움이 된다.

 

참고도서 : https://freelec.co.kr/book/1674/

 

[열혈강의] 자바 웹 개발 워크북

[열혈강의] 자바 웹 개발 워크북

freelec.co.kr

댓글