참고: 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;
}