<< 학습 목표 >>

1. 정적 쿼리와 동적 쿼리의 특징을 설명할 수 있다.

2. trim 태그를 활용할 수 있다.

3. if 태그를 활용할 수 있다.

4. choose, when, otherwise 태그를 활용할 수 있다.

5. if 태그와 choose, when, otherwiser 태그를 사용할 때 주의할 점을 설명할 수 있다.

6. foreach 태그를 활용할 수 있다.


지금까지 배운 것들만 가지고서도 충분히 내가 만들고 싶은 쿼리를 만들 수 있음

이번에는 알고 있으면 유용한 동적 쿼리에 대해서 배우자

 

우리가 지금까지 작성한 쿼리는 정적 쿼리임

 

다시 한번 전 글 ( https://codingaja.tistory.com/118 ) 에서 작성한 쿼리를 보자

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper
	PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
	"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.study.chapter04.StudygroupDao">
	<insert id="saveStudygroupInfo" parameterType="com.study.chapter04.StudygroupInfo" useGeneratedKeys="true" keyColumn="idx">
		INSERT INTO studygroupInfo(title, contents, regDate) VALUES(#{title}, #{contents}, CURRENT_TIMESTAMP());
	</insert>

	<insert id="joinStudygroupMember" parameterType="com.study.chapter04.StudygroupMember">
		INSERT INTO studygroupMember(studygroupIdx, memberIdx) VALUES(#{studygroupIdx}, #{memberIdx});
	</insert>
</mapper>

어느 상황에서든 INSERT 쿼리가 바뀔 일은 없음

INSERT 쿼리에 사용한 값은 바뀔 수 있지만 INSERT 쿼리의 구조 자체가 바뀔 순 없음

 

이렇게 언제 실행시키든 그 구조가 똑같은 쿼리를 정적 쿼리라고 함

 

 

이번에는 동적 쿼리의 예를 하나 들어보고 바로 동적 쿼리를 배워보자

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper
	PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
	"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.study.chapter04.StudygroupDao">
	<insert id="saveStudygroupInfo" parameterType="com.study.chapter04.StudygroupInfo" useGeneratedKeys="true" keyColumn="idx">
		INSERT INTO studygroupInfo(title, contents, regDate) VALUES(#{title}, #{contents}, CURRENT_TIMESTAMP());
	</insert>

	<insert id="joinStudygroupMember" parameterType="com.study.chapter04.StudygroupMember">
		<if test="idx == 0">
			INSERT INTO studygroupMember(studygroupIdx, memberIdx) VALUES(#{studygroupIdx}, #{memberIdx});
		</if>
		<if test="idx != 0">
			INSERT INTO studygroupMember(idx, studygroupIdx, memberIdx) VALUES(#{idx}, #{studygroupIdx}, #{memberIdx});
		</if>
	</insert>
</mapper>

id가 joinStudygroupMember 인 insert 태그를 보면 if문이 적용되 동적 쿼리를 만들었음

1. <if test="idx == 0">  ==>  전달 받은 DTO의 idx 멤버 변수 값이 0 이라면 if문 안에 있는 INSERT 쿼리가 실행됨

2. <if test="idx != 0">  ==>  전달 받은 DTO의 idx 멤버 변수 값이 0 이 아니라면 if문 안에 있는 INSERT 쿼리가 실행됨

 

전달 받은 DTO의 idx 멤버 변수 값이 0 이냐 아니냐에 따라 서로 다른 구조의 INSERT 문이 실행됨

전달 받은 DTO의 idx 멤버 변수 값이 0 이라면 studygroupIdx, memberIdx 칼럼만 활용하는 INSERT 쿼리문이 실행되고 전달 받은 DTO의 idx 멤버 변수 값이 0 이 아니라면 idx, studygroupIdx, memberIdx 칼럼을 활용하는 INSERT 쿼리문이 실행됨

 

MyBatis는 이런식으로 쿼리문에 자바처럼 조건문, 반복문 등을 활용해 동적 쿼리를 구성할 수 있음


동적 쿼리는 DML문 ( SELECT, INSERT, UPDATE, DELETE ) 에 사용할 수 있음

여기서 동적 쿼리를 구성하는 방법만 잘 익혀두면 DML문 모두 사용하는 방법은 똑같음


동적쿼리에 사용하는 태그는 아래와 같음

태그명 설명
trim 접두사(prefix), 접두어(suffix) 를 붙여주거나 지우는 태그
if 자바의 if와 같은 태그로 조건식이 참(true)인 경우 안에 있는 구문을 추가해주는 태그
choose 자바의 if ~ else if ~ else와 같은 태그로 if ~ else if ~ else 의 영역을 나타내는 태그
when 자바의 if ~ else 와 같은 태그로 choose 안에 사용하며 조건식이 참(true)인 경우 안에 있는 구문을 추가해주는 태그
otherwise 자바의 else와 같은 태그로 choose 안에 사용하며 모든 when의 조건식이 거짓(false)인 경우 otherwise 안에 있는 구문을 추가해주는 태그
foreach 자바의 for 또는 향상된 for문과 같은 태그로 반복적으로 구문을 추가해주는 태그

태그명 trim
설명 접두사(prefix), 접두어(suffix) 를 붙여주거나 지우는 태그

! 주의 !
일반적으로 trim은 앞, 뒤 공백을 "지워주는" 함수로 많이 사용함
이때문에 MyBatis의 trim도 무언가를 지워준다고 오해할 수 있는데 MyBatis의 trim은 지워주기도 하지만 concat 함수처럼 덧붙여줄 수도 있음
속성 1. prefix : trim 태그 내에 있는 구문의 맨 앞에 덧붙일 문자열

2. suffix : trim 태그 내에 있는 구문의 맨 뒤에 덧붙일 문자열

3. prefixOverrides : trim 태그 내 구문의 맨 앞에 해당 문자열이 있다면 지움

4. suffixOverrides : trim 태그 내 구문의 맨 뒤에 해당 하는 문자열이 있다면 지움

 

- prefix, suffix 속성 사용 예시

<insert id="insertMember" parameterType="com.study.chapter04.InsertMemberDto">
	INSERT INTO member(id, pw, nickname, tel) VALUES(#{id}, #{pw}, #{nickname}, #{tel});
</insert>

위와 같은 쿼리가 있을 때 prefix, suffix 를 사용해 아래와 같이 바꿀 수도 있음

<insert id="insertMember" parameterType="com.study.chapter04.InsertMemberDto">
	INSERT INTO member(id, pw, nickname, tel) VALUES
	<trim prefix="(" suffix=");">
		#{id}, #{pw}, #{nickname}, #{tel}
	</trim>
</insert>

! 주의 사항 !

가끔 이런 예시를 들면 "왜 저렇게 사용하나요?" 라는 의문을 갖는 분들이 있는데 저렇게 할 수도 있다는거지 반드시 저렇게 해야한다는게 아님

저렇게 할 수 있다는걸 알고 있으면 나중에 여러분의 상황에 맞게 바꿔서 사용할 수 있을 것

 

 

- prefixOverrides, suffixOverrides 속성 사용 예시

<delete id="deleteMember" parameterType="com.study.chapter04.DeleteMemberDto">
	DELETE FROM member WHERE id = #{id} AND pw = #{pw}
</delete>

위와 같은 쿼리가 있을 때 prefixOverrides, suffixOverrides 를 사용해 아래와 같이 바꿔도 실행되는 쿼리는 동일함

<delete id="deleteMember" parameterType="com.study.chapter04.DeleteMemberDto">
	DELETE FROM member WHERE
		
	<trim prefixOverrides="AND" suffixOverrides="OR">
		AND id = #{id} AND pw = #{pw} OR
	</trim>
</delete>

 

prefixOverrides 는 맨 앞에 있는 해당 문자열을 지워주므로 prefixOverrides 속성으로 인해 [ id = #{id} AND pw = #{pw} OR ] 가 됨

suffixOverrides 는 맨 뒤에 있는 해당 문자열을 지워주므로 suffixOverrides 속성으로 인해 [ id = #{id} AND pw = #{pw} ] 가 됨

 

 

prefixOverrides, suffixOverrides 속성은 | ( or ) 연산자를 사용할 수 있음

아래와 같이 | 연산자를 사용해서 ( 맨 앞에 있는 AND 또는 OR ) 를 지우고 ( 맨 뒤에 있는 AND 또는 OR ) 를 지울 수 있음

<delete id="deleteMember" parameterType="com.study.chapter04.DeleteMemberDto">
	DELETE FROM member WHERE
	
	<trim prefixOverrides="AND | OR" suffixOverrides="AND | OR">
		AND id = #{id} AND pw = #{pw} OR
	</trim>
</delete>

아직 배우진 않았지만 trim 태그를 잘 사용하면 where, set 태그를 대신할 수 있음


태그명 if
설명 자바의 if와 같은 태그로 조건식이 참(true)인 경우 안에 있는 구문을 추가해주는 태그

사용할 수 있는 연산자는 논리 연산자, 비교 연산자를 사용할 수 있음

논리 연산자의 && 는 and 로 사용하고 || 는 or 로 사용함
같다는 == , 다르다는 != 로 자바의 비교 연산자 문법과 동일함

그외에 문자와 문자열을 비교하는 방법은 MyBatis 만의 별도 문법이 있음

속성 1. test : 조건식이 들어가는 속성으로 이 속성에 들어있는 조건식의 결과가 true일 경우 if문 안에 있는 구문이 추가됨

 

- if 태그 사용 예시

<select id="selectMember" parameterType="String" resultType="com.study.chapter04.SelectMemberDto">
	SELECT * FROM member
		
	<if test="nickname != null">
		WHERE nickname = #{nickname}
	</if>
		
	;
</select>

<< 코드 설명 >>

if의 test 속성에 사용한 nickname은 파라미터로 전달 받은 DTO가 가지고 있는 nickname 멤버 변수의 값임

지금까지 INSERT, UPDATE, DELETE, SELECT 쿼리를 작성하며 파라미터로 전달 받은 DTO가 가지고 있는 멤버 변수에 접근할 때는 #{멤버변수} 를 사용했지만 test 속성에는 #{멤버변수} 가 아닌 멤버 변수명을 바로 쓴다는 점을 주의하자

 

- nickname 속성이 없을 경우 : SELECT * FROM member;

- nikcname 속성이 있을 경우 : SELECT * FROM WHERE nickname = "닉네임속성값";

 

으로 상황에 따라 서로 다른 구조의 쿼리가 만들어지므로 동적 쿼리 라고 함

 

 

그외에 몇 가지 예시를 더 보자

<select id="selectMember" parameterType="String" resultType="com.study.chapter04.SelectMemberDto">
	SELECT * FROM member WHERE nickname = #{nickname}
		
	<if test="start >= 0">
		LIMIT #{start}
		
		<if test="end != 0">
			, #{end}
		</if>
	</if>
		
	;
</select>

이 상황에서는 만들어질 수 있는 쿼리의 경우가 3가지임

- start가 0 미만인 경우 : SELECT * FROM member WHERE nickname = #{nickname}

- start가 0 이상이고 end가 0인 경우 : SELECT * FROM member WHERE nickname = #{ncikname} LIMIT #{start}

- start가 0 이상이고 end가 0이 아닌 경우 : SELECT * FROM member WHERE nickname = #{nickname} LIMIT #{start}, #{end}

 

 

이번에는 문자열을 비교하는 방법을 알아보자

MyBatis는 문자와 문자열을 구분하지 않고 모두 문자열로 처리함

따라서 문자를 표현할 때는 문자열 ( " ) 로 표현해야함

 

<select id="selectMember" parameterType="String" resultType="com.study.chapter04.SelectMemberDto">
	SELECT * FROM member WHERE nickname = #{nickname}
	
	<if test='orderby != null and orderby == "오름차순"'>
		ORDER BY idx ASC
	</if>
	<if test='orderby != null and orderby == "내림차순"'>
		ORDER BY idx DESC
	</if>
	
	<if test="start >= 0">
		LIMIT #{start}
	
		<if test="end != 0">
			, #{end}
		</if>
	</if>
	
	;
</select>

<< 코드 설명 >>

(1). 문자열 값을 표현할 때 " 를 사용해야하므로 test 속성의 값을 ' (홑따옴표) 로 감쌌음

  자바에서는 문자열 비교는 equals 메서드를 사용해야하지만 MyBatis는 == 연산자를 사용해도 같다, 다르다를 판단할 수 있음

 

문자열 비교할 때 아래와 같이 equals, equalsIsIgnoreCase 메서드를 호출할 수 있음

equals 메서드는 자바의 equals 메서드와 같음

equalsIsIgnoreCase 메서드는 대소문자를 무시하고 같은지를 판단할 수 있음

 


태그명 choose, when, otherwise
설명 choose - 자바의 if ~ else if ~ else와 같은 태그로 if ~ else if ~ else 의 영역을 나타내는 태그

when - 자바의 if ~ else 와 같은 태그로 choose 안에 사용하며 조건식이 참(true)인 경우 안에 있는 구문을 추가해주는 태그

otherwise - 자바의 else와 같은 태그로 choose 안에 사용하며 모든 when의 조건식이 거짓(false)인 경우 otherwise 안에 있는 구문을 추가해주는 태그
속성 choose 태그 - 속성 없음

when 태그 - test 속성 : if의 test 속성과 동일함

otherwise 태그 - 속성 없음

- choose, when, otherwise 태그 사용 예시

  if문에서 사용했던 예시를 choose, when, otherwise 의 상황에 맞게 바꾼 것

<select id="selectMember" parameterType="String" resultType="com.study.chapter04.SelectMemberDto">
	SELECT * FROM member WHERE nickname = #{nickname}
	
	<choose>
		<when test='orderby != null and orderby.equals("오름차순")'>
			ORDER BY idx ASC
		</when>
		<otherwise>
			ORDER BY idx DESC
		</otherwise>
	</choose>
    
	<if test="start >= 0">
		LIMIT #{start}
	
		<if test="end != 0">
			, #{end}
		</if>
	</if>
	
	;
</select>

<< 코드 설명 >>

(1). when과 otherwise는 choose 태그 안에만 사용할 수 있음

  마치 case 는 switch 안에만 사용할 수 있듯이...

  when은 if 내지는 else if의 역할을 하는 태그

  when의 test가 true라면 그 안에 있는 구문이 추가됨

  otherwise는 else 의 역할을 하는 태그

  모든 when의 test가 false라면 otherwise 안에 있는 구문이 추가됨

 

  if문에서도 else를 붙일 때 주의해야하는 것처럼 when, otherwise에서도 otherwise를 붙일 때 주의해야함

  위 코드는 문제가 생길 수 있는 코드임

  orderby 가 null이 아닌데 orderby 가 오름차순 이라면 ORDER BY idx ASC 구문이 붙지만 그외의 모든 상황에서는 ORDER BY idx DESC가 붙는 것임

  그외의 모든 상황이라는건 orderby가 null 이거나 orderby가 오름차순이 아닌 상황임

 

  if문에서 사용한 의도와는 전혀 다른 코드가 된 것

  틀렸다고 할 순 없지만 otherwise를 붙일 때는 조심하자

  if문에서 사용한의도와 완전히 같은 코드가 되려면 아래와 같이 바꿀 수 있음

 

choose, when, otherwise의 마지막으로 if문과 choose, when, otherwise를 비교해보자

(1). orderby가 null이 아닌데 orderby가 오름차순이라면 ORDER BY idx ASC 구문이 추가됨

  orderby가 null이 아닌데 orderby가 내림차순이라면 ORDER BY idx DESC 구문이 추가됨

(2). orderby가 null이 아닌데 orderby가 오름차순이라면 ORDER BY idx ASC 구문이 추가됨

  orderby가 null이 아닌데 orderby가 내림차순이라면 ORDER BY idx DESC 구문이 추가됨

 

위 if, choose, when, otherwise 를 해석해보면 이렇게 해석될 수 있을 것

그러나 여기서 if와 choose, when, otherwise는 큰 차이가 있음

 

if는 if 마다 마다가 개별적인 한 덩어리임

choose, when, otherwise는 coose 단위로 한 덩어리임

 

따라서 컴퓨터는 첫번째 if문의 test가 true이면 ORDER BY idx ASC 구문을 추가한 후 두 번째 if문 또한 체크함

지금 상황에서는 그럴리없지만 두 번째 if문의 test가 true라면 이어서 ORDER BY idx DESC 구문이 추가됨

이러면 문법적 오류가 발생함

if는 if 마다 개별적인 한 덩어리라는점에 주의하자

 

 

choose, when, otherwise 는 choose 안에 있는 한 when, 또는 otherwise 만 실행 되기 때문에 첫 번째 when의 test가 true이면 ORDER BY idx ASC 구문만 추가됨

첫 번째 when의 test가 false면 두 번째 when의 test를 체크하고 두 번째 when의 test가 true이면 ORDER BY idx DESC 구문만 추가됨

이렇게 choose, when, otherwise는 choose 단위로 한 덩어리라는 점에 주의하자


태그명 foreach
설명 자바의 for 또는 향상된 for문과 같은 태그로 반복적으로 구문을 추가해주는 태그
속성 - collection : 반복문에 사용할 반복 가능한 객체 / 배열, Map, List, Set 등과 같은 객체가 반복 가능한 객체임

- item : n번 째 반복에서 반복 가능한 객체 내 n번 째 요소(element) 를 전달 받을 변수명

- index : 반복 시 인덱스 번호가 필요할 때 지정하는 속성으로 인덱스 번호를 전달 받을 변수명

- open : 반복을 시작하기 전 추가할 구문

- close : 반복을 끝낸 후 추가할 구문

- separator : n번 째 반복이 끝난 후 추가할 구문

 

- foreach 태그 사용 예시

DAO가 쿼리 쪽으로 데이터들을 전달할 때 Map 보단 배열, List, Set 을 더 많이 활용하므로 Map인 경우는 설명하지 않음

또한 배열, List, Set은 모두 데이터들을 선형적으로 담고 있기 때문에 사용하는 방법 모두 동일함

 

<< SelectMemberDto >>

package com.study.chapter04;

import java.time.LocalDateTime;

import lombok.Data;

@Data
public class SelectMemberDto {
	private String id;
	private String pw;
	private String nickname;
	private String tel;
	private int idx;
	private LocalDateTime joinDateTime;
	private boolean isDel;
}

 

<< MemberDao >>

package com.study.chapter04;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface MemberDao {
	// ...
    
	SelectMemberDto selectMember(List<SelectMemberDto> selectConditions);
    
	// ...
}

<< 코드 설명 >>

id, pw, nickname, tel, idx, joinDateTime, isDel 멤버 변수를 갖고 있는 DTO가 있음

 

 

 

 

 

 

 

 

DAO에서는 id가 selectMember인 쿼리로 SelectMemberDto들을 담은 List를 전달하고 있음

전달하는 List의 이름은 selectConditions 임

 

 

 

 

 

 

이제 쿼리를 만들자

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
	PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
	"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.study.chapter04.MemberDao">
	// ...
    
	<select id="selectMember" parameterType="com.study.chapter04.SelectMemberDto" resultType="com.study.chapter04.SelectMemberDto">
		SELECT * FROM member WHERE nickname IN
		
		<foreach collection="selectConditions" item="condition" open="(" close=")" separator=", ">
			#{condition.nickname}
		</foreach>
		
		;
	</select>
    
	// ...
</mapper>

<< 코드 설명 >>

(1). DAO가 전달해주는 데이터들을 받기 위해서 데이터의 타입을 지정함

(2). 이 쿼리가 완성된 한 예를 보면 다음과 같음

  SELECT * FROM member WHERE nickname IN ("홍길동", "김철수", "고영희");

  IN 안에는 반복적으로 문자열이 들어가야하므로 foreach를 사용할 수 있음

  collection 속성은 DAO가 전달해주는 데이터들의 이름

  item 속성은 전달 받은 데이터들에서 첫 번째 데이터부터 마지막 데이터까지 차례대로 저장될 변수명

  open 속성은 반복문이 동작하기 전 추가될 문자열

  close 속성은 반복문이 동작한 후 추가될 문자열

  separator 속성은 n번째 반복이 끝난 후 추가될 문자열

 

만약 open, close 속성을 사용하지 않고 싶다면 다음과 같이 쿼리를 구성하면 됨

그러나 아래와 같이 구성하면 가독성이 많이 떨어진다는 걸 잘 알고 있을 것임

앞서 얘기했던 것처럼 꼭 이걸 써야된다 저걸 써야된다 는 없음

이런 저런 방법을 알고 있으면 내 상황에 맞는 코드를 작성할 수 있는 것

 

foreach의 특별한 점은 반복문이므로 특정 쿼리를 반복적으로 실행하도록 만들 수도 있음

INSERT 또는 UPDATE 또는 DELETE 를 굉장히 많이, 수천번 해야한다 면 아래와 같이 INSERT 쿼리문을 만들어 반복적으로 호출하면 될 것

 

<< 쿼리 >>

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
	PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
	"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.study.chapter04.StudygroupDao">
	// ...
	
	<insert id="joinStudygroupMember" parameterType="com.study.chapter04.StudygroupMember">
		INSERT INTO studygroupMember(studygroupIdx, memberIdx) VALUES(#{studygroupIdx}, #{memberIdx});
	</insert>
	
	// ...
</mapper>

 

<< DAO >>

package com.study.chapter04;

import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface StudygroupDao {
	// ...
	
	void joinStudygroupMember(StudygroupMember studygroupMember);
	
	// ...
}

 

<< 컨트롤러 >>

package com.study.chapter04;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class StudygroupController {
	@Autowired
	private StudygroupDao studygroupDao;
	
	@PostMapping()
	public void joinStudygroupMember(List<StudygroupMember> joinMemberList) {
		for(StudygroupMember joinMember : joinMemberList) {
			studygroupDao.joinStudygroupMember(joinMember);
		}
	}
}

이와 같이 컨트롤러에서 반복적으로 쿼리를 호출해 특정 쿼리를 반복적으로 실행할 수 있지만 굉장히 많이, 수 천번 반복적으로 호출하게 되면 서버 전체에 부하가 굉장히 클 수 있음

 

이렇게 특정 쿼리를 굉장히 많이 실행해야 되는 상황을 벌크(Bulk) 라고함

INSERT 를 많이 실행해야되는 상황을 Bulk INSERT

UPDATE 를 많이 실행해야되는 상황을 Bulk UPDATE

DELETE 를 많이 실행해야되는 상황을 Bulk DELETE

 

이럴 때는 DAO가 쿼리쪽으로 대량의 데이터를 전달하고 쿼리에서는 foreach 를 사용해 실행할 쿼리들을 한번에 생성하는 방법이 있음

 

<< 컨트롤러 >>

package com.study.chapter04;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class StudygroupController {
	@Autowired
	private StudygroupDao studygroupDao;
	
	@PostMapping()
	public void joinStudygroupMember(List<StudygroupMember> joinMemberList) {
		studygroupDao.joinStudygroupMember(joinMemberList);
	}
}

<< 코드 설명 >>

전과 다르게 컨트롤러에서 DAO로 대량의 데이터들을 한번에 전달함

 

package com.study.chapter04;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface StudygroupDao {
	// ...
	
	void joinStudygroupMember(List<StudygroupMember> studygroupMembers);
	
	// ...
}

<< 코드 설명 >>

DAO는 대량의 데이터를 받아 쿼리로 대량의 데이터를 전달함

이때 쿼리로 전달하는 대량의 데이터 이름은 studygroupMembers 임

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
	PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
	"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.study.chapter04.StudygroupDao">
	// ...
	
	<insert id="joinStudygroupMember" parameterType="com.study.chapter04.StudygroupMember">
		INSERT INTO studygroupMember(studygroupIdx, memberIdx)
	
		<foreach collection="studygroupMembers" item="studygroupMember" open="VALUES " separator=", " close=";">
			(#{studygroupMember.studygroupIdx}, #{studygroupMember.memberIdx})
		</foreach>
	</insert>
	
	// ...
</mapper>

<< 코드 설명 >>

쿼리에서는 전달 받은 대량의 데이터를 foreach로 반복하며 INSERT 쿼리를 구성함

 

위 insert 태그가 동작하면 다음과 같은 INSERT 코드가 만들어짐

INSERT INTO studygroupMember(studygroupIdx, memberIdx) VALUES (#{1번째 스터디그룹 idx}, #{1번째 사용자 idx}), (#{2번째 스터디그룹 idx}, #{2번째 사용자 idx}), (#{3번째 스터디그룹 idx}, #{3번째 사용자 idx}), ... , (#{n번째 스터디그룹 idx}, #{n번째 사용자 idx});

 

UPDATE, DELETE 또한 마찬가지로 foreach 를 사용하면 호출 한번으로 대량의 UPDATE, DELETE 가 되도록 할 수 있음

그러나 ! 앞서 언급했듯 "Bulk INSERT, UPDATE, DELETE 할 때 반드시 이 방법을 사용해야된다" 가아님

이 방법도 있다 임


이외에도 다른 예시를 보고 싶거나 더 자세한 사항들을 알고 싶다면 MyBatis 공식 홈페이지 ( https://mybatis.org/mybatis-3/ ) 에서 찾아보자

728x90
LIST