동적 SQL

Violet_Evgadn·2023년 4월 24일
0

DB연동

목록 보기
12/22
post-custom-banner

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
혹시 틀린 내용이 있다면 언제든 말씀해주세요!
post-custom-banner

0개의 댓글