Day 85. Spring Framwork 7 : Mybatis 동적 쿼리

ho_c·2022년 6월 26일
0

국비교육

목록 보기
66/71
post-thumbnail
post-custom-banner

MyBatis 동적 쿼리

지난 시간에는 MyBatis를 설치하고, CRUD를 구현하는 것까지 해봤다. 막상 해보고 나면 적당히 편해졌을 뿐, XML갔다가, DAO갔다 왔다 갔다 하는 것도 굉장히 귀찮다.

그래서 굳이? 이걸 쓰는 것에 대한 이유를 못 느꼈다.

그리고 수업 끝난 뒤, 이래서 쓰는구나 라고 느꼈다. 왜냐면 오늘은 동적쿼리를 만드는 방법을 배웠기 때문이다. 그럼 동적 쿼리는 뭘 말하는걸까?

별건 없다. 기존에 우리가 쿼리를 고정시키고 그 안의 값만 바꿔줬다면, 이제는 특정 조건에 따라서 동적으로 쿼리문 자체를 바꿔줄 수 있게 되는데, 이게 동적 쿼리다.

무튼 MyBatis의 동적쿼리 구현법은 크게 3가지가 있다.

1. <if test=“조건”>

가장 간단한 방법은 if로 조건에 따라서 쿼리 뒤 쪽으로 붙여주는 법이다.

<!-- 동적 쿼리 -->
<select id="selectByCon" resultType="kh.spring.dto.MessagesDTO">
	select * from messages 
	<if test="val!=null">
		where ${col} like '%'||#{val}||'%'
	</if> 	
</select>

위 텍스트는 select * from messages where ${col}=#{val} 라는 쿼리문을 동적 쿼리로 구현한 거다. 일단 SQL에서 조건은 where절로 붙는다. 따라서 DAO에서 값을 넘겨줄 때, 해당 키가 빈값이 아닐 때만, where절이 붙어 조건 검색이 가능하도록 했다.


2. <trim>

근데 <if> 의 한계는 조건식이 아주 간단할 때나 편리하다는 거다. 만약 아주 세밀하게 조건에 따라 하려면 자바에 쓸 if를 XML로 옮겨담는 것 뿐이다. 이걸 직접 세미에서 자바 프로그램 내에서 해봤는데, 최소 60개의 조건이 붙는다. 궁금하면 나의 '세미 회고'를 한번 찾아보자.

무튼 그런 복잡하고, 지저분한 방법에서 탈출하는 방법이 <trim>이다. trim은 쉽게 보면 미리 입력해준 쿼리문을 유동적으로 조작해주는 것이다. 특히 조건과 조건이 and로 연결되는 SQL에서는 이 기능이 제일 깔삼하다.

<select id="selectByMultiCon" resultType="kh.spring.dto.MessagesDTO">
	select * from messages
		
	<if test="writer !=  null ">
		where writer = #{writer}
	</if>
		
	<if test="content != null">
		and content = #{contents}
	</if>
</select>

직관적으로 알 수 있게끔, if만 사용한 지저분한 쿼리문을 가져와봤다. 각 조건을 구별했는데, 별반 문제 없어오비지만, writer가 없고, content 가 있다면 SQL 에러 바로난다. 또 content 기준으로 검색도 어렵다.

이럴 때 trim을 사용한다.

<select id="selectByMultiCon" resultType="kh.spring.dto.MessagesDTO">
	select * from messages
	<trim prefix="where" prefixOverrides="and|or">
		<if test="writer!=''"> <!-- DAO에서 넘겨받는 값 -->
			writer=#{writer}
		</if>
		<if test="content!=''">
			and content=#{content}
		</if>
	</trim>
</select>	

1) 안쪽에 태그가 있고 쿼리가 나올 때만 활성화 된다.

일단 trim 자체는 쿼리문을 뽑아서 뒤에 붙여준다고 생각하는게 편하다. 그래서 내부에는 쿼리를 뽑아줄 일종의 조건이 담긴 태그들이 있다.

그리고 포인트는 2가지가 있는 prefix, prifixOverriedes라는 속성이다.

2) 접두사 prefix = “where”

“뒤쪽에서 나온 Text 앞에 where을 붙여주세요”

DAO에서 넘겨준 값과 if의 조건에 따라서 trim 내부에서 text가 나오면 그 맨 앞에 ‘where’을 붙여주라는 것이다. 즉, 출력 시 자동으로 접두사를 붙여주는 건데 spring의 view resolver 와 비슷한 기능을 한다.

3) prefixOverriedes=“and”

“뒤쪽에 나올 text의 앞에 and가 나오면 없애주세요”

실행 순서상 prefixOverriedes 속성이 prefix보다 먼저 실행된다. 만약 조건이 충족돼 해당 쿼리문이 나오는데, 앞에 ‘and’가 붙어 있으면 그걸 자동으로 삭제하고 where을 붙여준다.

결과적으로 조건 자체는 각각 구별해서 확인한다. 거기서 참인 조건들만 모아서 순서대로 text 한 문장을 trim이 뽑아내는데, 이때 prefixOverriedes 조건으로 한번 거르고, prefix를 붙여줘서 쿼리를 날리는 것이다.

때문에 우리는 아주 쉽게 4개의 조건에 따른 쿼리문을 만들 수 있다.


3. <selectKey>

엄밀히 말하면 쿼리문을 조작하는 것이기 아니라서 동적쿼리라고 할 수 없다. 다만 동적으로 2가지 작업을 한 번에 처리해주는 편리한 기능이기 때문에 ‘동적 쿼리’ 파트에 넣어봤다.

상황을 하나 가정했다.

DB에서 하나의 값을 두 군데에 사용하는 경우가 은근 있다. 이때, 자바에서는 미리 뽑아놓고 분배한다. select seq.nextval from dual; 이 쿼리를 응용해서 말이다.

하지만 그 방식은 너무 불편하기 때문에 한번에 쿼리를 날려 값을 뽑아서 다음 쿼리를 넘기는 방법을 우리 모두 원한다. 그리고 이 기능을 MyBatis가 지원한다.

<insert id=“insert”>
	insert into messages values(messages_seq.nextval, #{writer}, #{content}, sysdate)
	<selectKey order=“AFTER” keyProperty=“seq” resultType=“int”>
		select messages_seq.currval from dual
	</selectKey>
</insert>

selectKey 태그는 3가지 속성이 세팅된다.

1) order

order의 값을 해당 쿼리가 실행되는 시점을 의미한다. 그 값으로 ‘BEFORE’, ‘AFTER’을 세팅해서 자신이 속한 쿼리의 실행 시점을 정해줄 수 있다.

2) keyProperty

resultType과는 약간 다른 개념으로, 쉽게 생각하면 값을 받을 바구니를 미리 세팅해주는 것이다. 예를 들어 resultType이 ‘물’이라면 ‘물통’을 준비하는거라 생각하면 된다.

그래서 반환값이 DTO면 DTO이고, 그 안에 멤버필드에 값을 세팅해주는 것이다. 이 원리는 Call by references 기법으로 가능하다.

물론 복잡하지만 Map도 지원하기 때문에 Map으로 받아서 key를 세팅하면된다. 참고로 select을 Map으로 하면 반환값(Object)이니, 형변환해서 사용해주자.

3) resultType

앞서 말한 것처럼, 물통이니까 물을 담아야 한다. 그러니 담아올 것이 물이라는 것을 알려주는 것이다.

값을 넣어주고 insert / 값을 출력하고 select 의 과정에 사용되는 DTO는 한 개다.
이 원리는 Call by references 기법으로 가능하다.

profile
기록을 쌓아갑니다.
post-custom-banner

0개의 댓글