동적 SQL

Violet_Evgadn·2023년 4월 24일
0

DB연동

목록 보기
12/22

if

MyBatis 동적 SQL에서 가장 널리 활용되는 것이 if가 아닐까 생각한다.

MyBatis에서 결국 동적 SQL은 "어떤 값이 NULL일 때는 조건문에 추가시키지 말고, 값이 존재할 때만 조건문에 추가시켜주세요" 같은 요청을 처리할 때 많이 활용되고 이를 처리하기 위한 가장 적절한 동적 SQL이기 때문이다.

WHERE문에 많이 활용되기는 하지만 ORDER BY, GROUP BY 등에서도 <if>를 활용할 수 있다.

간단한 if 활용

<select id="findActiveBlogLike" resultType="Blog">
  SELECT 
  	* 
  FROM 
  	BLOG 
  WHERE state = ‘ACTIVE’
  <if test="title != null">
    AND title like #{title}
  </if>
  <if test="author != null and author.name != null">
    AND author_name like #{author.name}
  </if>
</select>

분명 MyBaits로 짰는데 신기하게도 무슨 말인지 바로 이해할 수 있을 것 같다.

만약 <if> 태그에서 test 부분에 조건문이 true를 반환한다면 태그 사이에 존재하는 Query문을 활용하고, 만약 false라면 태그 사이에 존재하는 Query문을 활용하지 않는 것이다.

위 코드를 통해 설명해보자면, title이라는 값이 null이 아닐 경우 "AND title like #{title}"문을 WHERE 조건에 추가시켜 Data의 title 중 입력값 title을 포함하는 데이터만 Select 한다는 의미가 될 것이다.

또한 입력값 author과 author.name이 동시에 null이 아니라면 입력값 author.name이 포함된 데이터를 추가로 Select 한다는 의미가 될 것이다.

여기에서 알아야 할 점은 "#{title}"처럼 활용하지 않는다는 것이다. 원래 MyBatis는 객체의 데이터를 가져오기 위해서는 Parameter를 활용해 #{}나 ${}를 활용해야만 했다.

하지만 <if>, 그리고 아래에 나오는 모든 동적 SQL 태그에서는 Parameter 처리를 자동으로 해주기 때문에 단순히 멤버 변수의 이름과 일치하게만 코드를 짜주면 알아서 처리해준다는 것을 알고 있자. (오히려 #{}, ${}을 추가하면 에러가 발생한다!)


choose, when, otherwise

자바에 Switch문이 존재한다면, MyBatis에서는 choose Element가 존재한다.

if문과 사용 방법은 비슷하지만, Switch 문과 유사하기 때문에 조건에 따라 선택하는 Query를 다르게 할 수 있다는 장점을 가진다.

태그를 통해 Switch문임을 알리고, 태그를 활용해 case를 지정해준다.

이 때 태그의 test Parmeter를 통해 test = true일 경우에 해당 태그 사이에 존재하는 Query를 활용하게 된다.

마지막으로 를 통해 Switch문의 default문을 생성할 수 있다

<select id="findActiveBlogLike" resultType="Blog">
  SELECT 
  	* 
  FROM 
    BLOG 
  WHERE state = ‘ACTIVE’
  <choose>
    <when test="title != null">
      AND title like #{title}
    </when>
    <when test="author != null and author.name != null">
      AND author_name like #{author.name}
    </when>
    <otherwise>
      AND featured = 1
    </otherwise>
  </choose>
</select>

<where>

만약 WHERE문 이후 if문만 존재한다면 AND나 OR로 시작해버리는 조건문이 생성될 수도 있고, 아예 조건이 없는 Query가 생겨 에러가 발생할 수 있다.

예를 들어 WHERE <if test="false"> title = #{title} </if>로 Query문을 짰다면 최종적으로 Query는 "SELECT * FROM X WHERE"이라는 미완성된 Query가 될 것이고, 이는 당연히 에러를 발생시킬 것이다.

또한 WHERE title <if test="false"> title = #{title} </if> <if test="true"> AND name = #{name}</if>로 존재한다면 SELECT * FROM X WHERE AND name=홍길동라는, WHERE AND라는 이상한 값이 Query에 포함될 것이다.

<where> Element는 태그에 의한 컨텐츠가콘텐츠가 1개라도 리턴되면 WHERE문을 추가되고, 콘텐츠가 하나도 리턴되지 않으면 WHERE문을 추가시키지 않는다.

또한 WHERE문을 형성했을 때 WHERE 이후에 AND나 OR이 존재하면 이 값을 제거하고 Query를 형성한다

<select id="findActiveBlogLike" resultType="Blog">
  SELECT * FROM BLOG
  <where>
    <if test="state != null">
         state = #{state}
    </if>
    <if test="title != null">
        AND title like #{title}
    </if>
    <if test="author != null and author.name != null">
        AND author_name like #{author.name}
    </if>
  </where>
</select>

<where>이 SELECT문에 활용되는 값이라면, <update>에서 활용할 수 있는 <set>도 존재한다. set 태그는 값을 모두 주입하는 것이 아닌 일부분만 주입하고 싶을 경우 활용하는 태그이다.

<where>은 AND와 OR이 문제였다면 UPDATE에서는 쉼표(,)가 문제가 된다. <set> 태그는 Query문 끝에 있는 쉼표를 자동으로 처리하여 정상적인 Query문이 되도록 바꿔준다

<update id="updateAuthorIfNecessary">
  update Author
    <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
  where id=#{id}
</update>

trim은 User가 직접 설정하여 위와 같이 특정 구문을 삭제할 수 있게 만드는 것이다.

이 trim을 통해 <where><set>도 구현할 수 있다.

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
</trim>

위와 같이 trim을 활용하면 prefix가 AND나 OR로 들어올 경우 제거하고, prefix는 WHERE이기 때문에 WHERE문으로 활용한다는 것이다. 즉 <where>과 같은 역할을 할 수 있는 것이다

<trim prefix="SET" suffixOverrides=",">
  ...
</trim>

또한 위와 같이 trim을 활용하면 prefix가 SET이므로 UPDATE문의 SET 절을 추가함을 알 수 있고, suffixOverrides가 ","이기 때문에 가장 마지막에 ","가 들어올 경우 이를 제거하여 정상적인 Query문을 형성해주는 <set> 태그라는 것을 알 수 있다.

<trim><where>이나 <set> 위치에 대신 입력만 해주면 그대로 활용할 수 있게 된다


foreach

개인적으로 나는 많이 활용하는데, 그렇게까지 중요시 생각지는 않는 것 같다.

주로 List형식으로 한 번에 데이터를 넣어줘 Query를 실행하고 싶을 때 많이 활용하며 Java collection에 대한 반복처리를 수행해줘야 할 때 많이 활용한다.

<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM 
  	POST P
  <where>
    <foreach item="item" index="index" collection="list" 
    	open="ID in (" separator="," close=")" nullable="true">
          #{item}
    </foreach>
  </where>
</select>

item을 통해 어떤 id 값으로 활용할 것인지를 정하고, index를 통해 list index를 받을 수 있으며 collection을 통해 어떤 객체를 가지고 올 것인지 명시할 수 있다.

또한 Separator를 통해 for문의 각각 시행에 대해 구분자를 나타낼 수 있으며, open과 close를 활용해 1번의 for문의 시작과 끝 부분을 설정해줄 수 있다.

마지막으로 nullable "true"로 설정하면 NULL 값이 올 수 있게 설정할 수도 있다

profile
혹시 틀린 내용이 있다면 언제든 말씀해주세요!

0개의 댓글