참고: DB 로는 PostgreSQL 을 사용 중입니다!
아래와 같은 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('%', '%', '%')
쿼리 실행:
그렇다면 MyBatis 를 사용한 like 연산자를 알맞게 사용하는 방법은 뭐가 있을까요?
저의 경우에는 2가지 방법을 사용하는데, 지금부터 해당 방법을 공유해보겠습니다.
<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>
name 에 바인딩된 문자열에 정규식 연산을 수행하여%,_ 문자 앞에 "\"가 붙도록 합니다.<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>
<bind> 태그를 하나 추가합니다.UserDTO 의 'name' 필드)을 넣어줍니다. <bind> 의 value 속성에서 java method 를 호출하여"name" 에 바인딩되는 문자열을 치환합니다.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;
}
}public static 으로 선언되어야 합니다.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;
}