[MyBatis] %, _ 가 포함된 문자열의 like 연산

식빵·2023년 3월 2일
0

Mybatis

목록 보기
2/9
post-thumbnail

참고: DB 로는 PostgreSQL 을 사용 중입니다!


😥 무용지물이 되버린 Where


아래와 같은 user 테이블이 있다고 가정해봅시다.


위 테이블에서 name column 을 기준으로 검색을 수행하려면 MyBatis Mapper Xml
어떻게 작성할까요? 아마 대부분 아래처럼 작성할 겁니다.

<select id="search" parameterType="UserDTO" resultType="UserVO">
  select * from coding_toast."user"
  <where>
    <if test="@org.springframework.util.StringUtils@hasText(name)">
      name like concat('%', #{name}, '%')
    </if>
  </where>
  -- 페이징, 정렬 sql 생략...
</select>

그런데 문제가 있습니다.
만약에 사용자가 name 컬림이 "Some O%ne" 인 row 를 검색하기 위해,
#{value} <-- "%" 처럼 바인딩하면 어떻게 될까요?

아마 MyBatis 는 동적으로 아래와 같은 쿼리르 생성 및 실행하게 되어서
결국 원치 않는 모든 user row 정보를 얻게 될 겁니다.

select * from user
where name like concat('%', '%', '%')

쿼리 실행:

  • 필터링을 위해 where 절을 사용하는 건데, 이러면 안되겠죠?



😉 해결법


그렇다면 MyBatis 를 사용한 like 연산자를 알맞게 사용하는 방법은 뭐가 있을까요?
저의 경우에는 2가지 방법을 사용하는데, 지금부터 해당 방법을 공유해보겠습니다.

1. SQL 로만 해결하기

<select id="search" parameterType="UserDTO" resultType="UserVO">
  select * from coding_toast."user"
  <where>
    <if test='@org.springframework.util.StringUtils@hasText(name)'>
		name like concat('%', regexp_replace( #{name}, '([%_])', '\\\1','g'), '%')
    </if>
  </where>
</select>
  • PostgreSQL 의 regexp_replace 연산자를 통한 해결법입니다.
  • name 에 바인딩된 문자열에 정규식 연산을 수행하여
    %,_ 문자 앞에 "\"가 붙도록 합니다.

2. MyBatis 의 <bind> 사용하기

<select id="search" parameterType="UserDTO" resultType="map">
  <bind 
	 name="name" 
     value='@util.EscapeHelper@escape(name)'
  />
  select * from coding_toast."user"
  <where>
    <if test='@org.springframework.util.StringUtils@hasText(name)'>
      name like concat('%', #{name}, '%')
    </if>
  </where>
</select>
  1. mybatis 에서 제공하는 <bind> 태그를 하나 추가합니다.
    bind 태그의 name 속성에는 우리가 치환할 파라미터
    명칭(UserDTO 의 'name' 필드)을 넣어줍니다.

  1. 이때 <bind>value 속성에서 java method 를 호출하여
    "name" 에 바인딩되는 문자열을 치환합니다.

  1. value 구문 안에서 EscapeHelper.escape 메소드를 호출하는데,
    해당 메소드의 내용은 아래와 같습니다.
    package util;
    import org.springframework.util.StringUtils;
     
     public class EscapeHelper {
     		public static String escape(String s) {
    			return StringUtils.hasText(s) ? 
             		s.replaceAll("([%_])", "\\\\$1") : s;
    		}
      }
    • 주의!! method 는 반드시 public static 으로 선언되어야 합니다.

  1. (참고) parameterType = UserDTO 클래스 코드는 아래와 같습니다.
    package coding.toast.playground.user.domain;
     
     import lombok.*;
     import org.apache.ibatis.type.Alias;
     
     @Alias("UserDTO")
     @Getter @Setter @ToString
     public class UserDTO {
        private Long id;
        private String name;
        private String phoneNumber;
     }



✨ 참조링크

profile
백엔드를 계속 배우고 있는 개발자입니다 😊

0개의 댓글