https://velog.io/@joyfuljoyful/MyBatis-%EB%8F%99%EC%A0%81%EC%BF%BC%EB%A6%AC-trimwhereset
https://java119.tistory.com/90
스프링에서 Mybatis를 사용하다보면 동적으로 쿼리를 작성해야 할 때가 잇음
그때 조건문에 동적으로 들어가야할 값이 아래와 같다고 치자
=================================================================================================================
SELECT * FROM
WHERE A=##{A}
AND B=##{B}
AND C=##{C}
=================================================================================================================
A에는 값이 무조건 있고
B , C 에 값이 있을 수 있고 전부다 없을 수 있다고 한다면 이걸 IF문으로만 작성했을 경우 아래와 같다
=================================================================================================================
SELECT * FROM
WHERE A=##{A}
<if test='A!=null'>
AND B=##{B}
</if>
<if test='A!=null'>
AND C=##{C}
</if>
=================================================================================================================
근데 만약 A에도 값이 있을 수도 있고 없을 수도 있다면 A에도 IF문을 적용해야 한다.
=================================================================================================================
SELECT * FROM
<if test='A!=null'>
WHERE A=##{A}
</if>
<if test='B!=null'>
AND B=##{B}
</if>
<if test='C!=null'>
AND C=##{C}
</if>
=================================================================================================================
위와같이 작성한다면 A가 없을 경우 쿼리문은 아래와 같이 잘못된 쿼리문이되어 실행조차 안된다
=================================================================================================================
SELECT * FROM
AND B=##{B}
AND C=##{C}
=================================================================================================================
여기서 혹시나 그냥 널체크하지말고 해도대지않나?
싶을텐데 아래의 조건은 완전 다른조건이다. 들어가는 순간 쿼리에서 구하고자 하는 값은 완전히 바뀐다
SELECT * FROM WHERE B=null
SELECT * FROM
if문으로 좀더 분기해서 where 이 들어가도록 쿼리문 짜면 안되나? 라고 생각할수 있음
if문으로 분기한게 아래의 쿼리임, 복잡한거 봐라..
=================================================================================================================
SELECT * FROM
<if test='A!=null'>
WHERE A=##{A}
</if>
<if test='B!=null and A!=null'>
AND B=##{B}
</if>
<if test='B!=null and A==null'>
WHERE B=##{B}
</if>
<if test='C!=null AND B!=null'>
AND C=##{C}
</if>
<if test='C!=null AND B==null AND A!=null'>
AND C=##{C}
</if>
<if test='C!=null AND B==null AND A==null'>
WHERE C=##{C}
</if>
=================================================================================================================
이와같은 방안을 해결하기 위해 사용하는 방법중 where 1=1 조건이 있다
해당 조건은 무조건 true 이므로 그 뒤에 올 조건문들은 AND 또는 OR로 시작하면 된다
(연산자에서 사용할 AND , OR 의 순서는 알아서 잘 짜자...)
이방법으로 쿼리를 짜면 아래와 같다
=================================================================================================================
SELECT * FROM
WHERE 1=1
AND A=##{A}
AND B=##{B}
AND C=##{C}
=================================================================================================================
위의 쿼리를 Mybatis의 IF문을 적용하면 아래의 쿼리문이 완성된다
IF문 안에 들어가는 내용들이 전부다 null이든, 하나만 있던 정상적인 쿼리가 출력된다
A , B , C 가 전부다 null 일 경우에는 SELECT * FROM WHERE 1=1 이라는 쿼리로 실행이 되며 오류가 발생하지 않음
=================================================================================================================
SELECT * FROM
WHERE 1=1
<if test='A!=null'>
AND A=##{A}
</if>
<if test='B!=null'>
AND B=##{B}
</if>
<if test='C!=null'>
AND C=##{C}
</if>
=================================================================================================================
뭐 혹시나 UPDATE , DELETE 에서 WHERE 1=1 때문에 모든 데이터가 실행되버리면 어캐해요? 라고생각할수 있음
아래의 쿼리문을 보자
=================================================================================================================
UPDATE 테이블명 SET
D = ##{D}
WHERE 1=1
<if test='A!=null'>
AND A=##{A}
</if>
<if test='B!=null'>
AND B=##{B}
</if>
<if test='C!=null'>
AND C=##{C}
</if>
=================================================================================================================
위의 쿼리문을 실행할때 A , B , C 가 전부다 null 이라고 가정한다면
UPDATE 테이블명 SET D = ##{D} WHERE 1=1 이 실행되면서 모든 데이터가 변경되버리는 대참사가 발생하잖아!! 라고 생각 할거임
근데 UPDATE , DELETE 쿼리문을 할때는 WHERE 1=1 문제가 아니라 쿼리문을 보내기 전에 JAVA 코드로 검증먼저 하는게 옳바른거임
1=1 문제따지기전에 아래의 쿼리처럼 IF문으로 분기해서 작성했어도 위와같이 UPDATE 테이블명 SET D = ##{D} WHERE 1=1 실행됨
UPDATE , DELETE 하는데 WHERE 부터 IF가 드가는건 말이 안됨
UPDATE , DELETE 에서의 조건에는 유니크한값 또는 키값이 들어가야지...뭐 여러개 업데이트 친다고하면 명확한 조건이 기본적으로 들어가야함
=================================================================================================================
UPDATE 테이블명 SET
D = ##{D}
<if test='A!=null'>
WHERE A=##{A}
</if>
<if test='B!=null and A!=null'>
AND B=##{B}
</if>
<if test='B!=null and A==null'>
WHERE B=##{B}
</if>
<if test='C!=null AND B!=null'>
AND C=##{C}
</if>
<if test='C!=null AND B==null AND A!=null'>
AND C=##{C}
</if>
<if test='C!=null AND B==null AND A==null'>
WHERE C=##{C}
</if>
=================================================================================================================
위와같이 작성했을때 크게 문제가 되는 부분은 없음
그러나 Mybatis 에서는 동적쿼리에 사용할수 있는 기능들이 더있음
<trim> <where><set> 이 있음
위에서 작성한 WHERE 1=1 대신에 <where> 를 넣으면 됨
그리고 A!=null 이 걸리면 AND 가 실행되서 WHERE AND 가 되는거 아니냐고 물어볼수 있는데
WHERE AND가 되지 않고 알아서 문법에 맞게 치환 해줌
=================================================================================================================
SELECT * FROM
<where>
<if test='A!=null'>
AND A=##{A}
</if>
<if test='B!=null'>
AND B=##{B}
</if>
<if test='C!=null'>
AND C=##{C}
</if>
</where>
=================================================================================================================
아래는 실제로 사용했던 쿼리임
trim 을 사용해서 where 문을 작성했었음
=================================================================================================================
SELECT
stock_code ,parsing_date_detail ,parsing_date ,stock_name ,stock_category_code
,stock_category_name ,present_price ,yesterday_price ,current_price ,high_price
,upper_limit_price ,low_price ,lower_limit_price ,trading_volume ,trading_value
,direction ,direction_code ,price_gap ,foreign_trade ,institution_trade ,parsing_memo
FROM parsing_data
<trim prefix="WHERE" suffixOverrides="AND">
<if test="stockCode!=null"> stock_code = ##{stockCode} AND </if>
<if test="stockName!=null"> stock_name = ##{stockName} AND </if>
<if test="stockCategoryCode!=null"> stock_category_code = ##{stockCategoryCode} AND </if>
<if test="stockCategoryName!=null"> stock_category_name = ##{stockCategoryName} AND </if>
</trim>
=================================================================================================================
prefix 는 trim 안에서 생성되는 쿼리문의 제일 앞에 붙여주고, suffixOverrides 는 생성되는 쿼리문의 제일뒤에 있는 단어를 제거함
위의 쿼리문을 토대로 예시를 작성해봄
=================================================================================================================
SELECT * FROM parsing_data
<trim prefix="WHERE" suffixOverrides="AND">
<if test="A!=null"> A = ##{A} AND </if>
<if test="B!=null"> B = ##{B} AND </if>
<if test="C!=null"> C = ##{C} AND </if>
<if test="D!=null"> D = ##{D} AND </if>
</trim>
=================================================================================================================
정확히 어떻게 작동하나면 전부다 NULL이 아닐경우엔 아래의 쿼리가 실행됨
trim 에서 생성된 문자들
A=##{A} AND B=#{B} AND C=#{C} AND D=#{D}AND 에서 제일뒤에 있는 AND를 지우고,
제일앞에는 WHERE 를 붙여주는거임
=================================================================================================================
SELECT * FROM parsing_data
WHERE
A=##{A} AND
B=##{B} AND
C=##{C} AND
D=##{D}
=================================================================================================================
<set> 도 비슷함 값이 있으면 해당문 바로앞에 SET 을 붙여줘서 생성함
=================================================================================================================
UPDATE 테이블명
<set>
<if test="E!=null"> E = ##{E} </if>
</set>
WHERE A=##{A}
=================================================================================================================
=================================================================================================================
UPDATE 테이블명
SET E=##{E}
WHERE A=##{A}
=================================================================================================================