본문 바로가기
[ Developer ]/Spring Framework

[Spring] 스프링 다이나믹 쿼리 Dynamic Query

by 김현섭. 2016. 6. 13.
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.
Dynamic Query
Spring의 장점인 쿼리에서 조건문을 통해서 다른 조건이 대입해서 중복되는 쿼리들을 최소화 시킬 수 있다
문법은 JSTL과 비슷하다 

# Dynamic Query 종류
  • if
  • choose
    • java의 else if 격
  • trim
  • foreach
    • 잘 사용되지 않지만 가끔 사용할 곳이 있다

# IF
MyBatis에서 가장 많이 사용되는 요소, 파라미터로 받아온 값을 조건절로 걸러서 조건절 등을 추가할 때 사용한다
파라미터 타입 안에 다른 타입(클래스)가 포함되어 캡슐화를 이룰 경우 .(dot) 연산자로 변수 접근이 가능하다

# Choose
else if 가 없기 때문에 choose로 사용된다
<choose></choose>로 로 크게 감싸고 <when test="조건"> </when>으로 조건을 걸고 <otherwise> </otherwise>로 기타를 거른다

# Trim (where, set)
여러 구문 중 하나만 실행
SELECT *
FROM   BLOG
WHERE
<if test="state != null">
     state = #{state}
</if>
<if test="title != null">
AND    title like #{title}
</if>

위와 같은 쿼리가 있다면 모든 조건이 해당되지 않아 빠져 나온다면
SQL 자체가 문법상 제대로 형성되지 않아서 SQL 문법 에러가 발생할 수 있다

그러한 단점을 해결하기 위해서 Trim을 사용한다

trim을 이용한 쿼리는 다음과 같다
1
2
3
4
5
6
7
8
9
10
SELECT *
FROM   BLOG
<where>
     <if test="state != null">
          state = #{state}
     </if>
     <if test="title != null">
          AND title like #{title}
     </if>
</where>
cs

WHERE 조건을 자동으로 생성하며 문법상 맞게 맞춰준다 MyBatis가 AND 나 OR를 자동으로 붙여주진 않으므로
AND나 OR 조건은 정의를 해줘야한다 

UPDATE 시에도 set 이며 컬럼명이 나오고 이후 컬럼들은 , 콤마를 붙이기 때문에 같은 문제가 발생할 수 있다
그렇기 때문에 trim을 사용하면 된다
set을 사용할 때는 콤마를 뒤에 찍는다

<trim prefix="WHERE" prefixOverrids="AND">로 trim을 사용한다면 자동으로 AND를 붙여준다

업데이트도 동일하게 사용이 가능한데
<trim prefix="SET" suffix Overrids=",">
을 통해서 자동으로 SET 뒤에 콤마를 붙여준다

# ForEach
Dynamic Query에서 공통적으로 필요한 기능은 Collection의 반복 처리 기능

1
2
3
<foreach collection="array" item="배열명" separator=",">
     #{배열명}
</foreach>
cs

Collection에는 배열이나 리스트만 사용이 가능하다 item은 파라미터 명을 적어주면 되고
Separator는 안에 담긴 Object들을 구분 시켜줄 기호이다

# IF 실습


위와 같이 if를 통해서 조건을 걸 수 있다
또한 SELECT에 보면 주석이 있는데 어떤 DAO에서 사용하는 건지와 작성 날짜를 적어주면 좋다

쿼리 결과를 확인하기 위해서 ArticleDAOImpl에서 FirstName을 주석 처리한다면 조건이 사라진 쿼리로 실행이된다
그렇기 때문에 하나의 쿼리로 두가지 상황의 쿼리를 생성할 수 있다


결과는 다음과 같이 firstName을 주석 처리해서 없으니 컬럼들이 조건절에 따라서 없어진 결과를 보여준다


또 하나를 응용하자면 DEPARTMENTS에서 부서명을 더 가져올 수 있다


위와 같이 작성한다면 firstName이 있을 때 D.DEPARTMENT_NAME을 출력하고
아래와 같이 조건 조인을 걸 수 있다

departmentName을 받을 수 있게 EmployeesVO에서 변수를 추가해준다


그런 후 jsp에서 출력하는 구문을 추가하면 된다


그런 후 결과를 보면 정상적으로 출력이 되는 것을 볼 수 있다


# Choose 실습
if를 chooose when otherwise로 바꿔본다

SELECT에서 컬럼을 가져오는 구문에서 사용된 IF를 choose로 바꿔본다


잘 되는 것을 볼 수 있다


이제는 otherwise를 걸어본다


위와 같이 작성해서 firstName이 없다면 DEPARTMENT_NAME을 제외하고 다 가져오면 된다
그런 후 테스트를 위해서 firstName을 주석처리 해본다
그럼 결과는 다음과 같이 나오게 된다


Department Name이 사라진 것을 볼 수 있다
이제는 when 절을 하나 더 걸어서 두 개의 조건으로 어떠한 쿼리가 생성되나 테스트 해본다


현재는 firstName이 없으므로 2번의 쿼리가 실행이 될 것이다
결과는 다음과 같다


그렇다면 이제는 첫 번째 when과 두 번째 when이 모두 참일 경우 어떤 쿼리가 실행되는지를 체크해본다


결과는 다음과 같이 첫 번째 when 절만 실행된 것을 볼 수 있다
when에서 첫 번째로 걸린 쿼리를 사용하는 것을 알 수 있다

# Trim 실습

Trim을 실습하기 위해서 조건 절에서 LAST_NAME을 조건절로 빼본다


테스트를 진행하기 위해서 lastName을 주석처리 시키고 /list로 접근을 해본다


에러가 발생한 것을 볼 수 있다 에러가 발생한 쿼리를 보면 WHERE 절 이후에 lastName이 없어서 LastName이 없고 바로 AND가 나온 것을 알 수 있다 이렇기 때문에 SQL 문법 에러가 발생한 것이다

또한 firstName까지 주석을 해본다


그런다면 다음과 같이 에러가 발생한다


위와 같은 에러를 잡기 위해서는 단순한 해결 방식도 있다


쿼리에서 WHERE 절에 1=1이라는 무조건 참 값을 주고 그 외의 추가 조건을 모두 AND를 붙여버리고 IF로 나누는 것이다
그렇다면 WHERE절에서는 무조건 에러가 발생하지 않고 AND를 통해서 조건절에서 참 인것만 추가가 되게 된다

하지만 위와 같은 방식은 사용하지 않고 Trim을 사용해서 해결한다
다음과 같이 where로 크게 if구절들을 묶어 준다


위와 같이 작성된다면 if 구절에 참이 없다면 WHERE가 붙지 않게 된다
그리고 firstName의 주석을 지우고 다시 실행을 해본다
그렇다면 쿼리는 다음과 같이 작성된 것을 볼 수 있다


그렇다면 이제 firstName을 주석처리 하고 lastName을 주석을 지원본다


위와 같이 자동으로 필요한 WHERE를 붙여주는 것을 볼 수 있다

더욱 편하게 사용을 하려면 trim을 사용하는 경우도 있다
AND를 작성하지 않고 자동으로 붙게 해주게 사용을 할 수 있다


위와 같이 3가지 방식이 있지만 실무에서는 쓰는 방식이 다르다
WHERE 1=1을 사용하는 것이 편리하고 실수할 확률이 적다 하지만 실무에서 WHERE 1=1을 사용하지 않는다면
<where>을 사용하는 것이 편리하다

# ForEach 실습
forEach 실습을 위해서 firstName과 lastName을 모두 주석 처리한다

ArticleDAOImpl.java



WHERE 절 이후에 다음과 같이 작성한다
close와 open이 있는데 open에는 "(" close에는 ")"를 넣어주면 된다


아래와 같이 작성을 해서 List로 비교를 하며 MANAGER_ID에 포함되는 지를 넣어주면 된다


그리고 List를 넣어주기 위해서 생성해야 하는데 ArticleDAOImpl.java에서 생성을 해준다


위와 같이 작성되면 2개의 파라미터를 보내야한다 이런 상황에서 Map을 이용해서 넣으면 된다
다음과 같이 변경을 해주면 된다


그리고 쿼리에서 parameterType을 map으로 변경해주면 된다

이렇게 된다면 밑에있는 firstName과 같은 변수들이 모두 에러가 발생한다
바로 맵으로 넘어왔기 때문인데 employee의 Key 값으로 보냈던 객체를 이용해서 접근하면 된다
조건절에 있는 객체 변수들 앞에 key값을 붙여준다


완료된 쿼리는 다음과 같다


결과는 다음과 같이 나온다


실행된 쿼리는 다음과 같은 모양으로 작성이 되어 있다