VO가 필드로 가지는 자료형이 기본자료형일 경우
resultMap을 통해 컬럼명과 VO의 필드명을 매핑시켜주면
간단한 처리가 가능해지는 매핑구문은 마이바티스의 가장 큰 장점이다.
그런데, 필드에 기본자료형이 아닌 컬렉션 또는 다른 VO를 받아야 한다면?
자바 코드로 반복문을 수행해서 값을 하나씩 가져올 수도 있겠지만,
이때 마이바티스 resultMap에서 유용하게 쓸 수 있는 태그들이 있다.
< Collection >
- has many 관계를 처리하기 위해 사용
* 마이바티스에 따르면, 두 가지 방법으로 collection을 작성할 수 있다.
1. collection을 위한 내포된(Nested) Select : collection으로 값을 넣어주기 위한 select문을 별도로 작성
2. collection을 위한 내포된(Nested) Result : collection내에 조회 결과 컬럼들을 작성하여 직접 매칭
결과 매핑의 재사용성이 높은 select문 방식을 선호하는 것 같지만, 간단한 매칭의 경우 result로도 가능하다.
마이바티스는 관계를 정의하는 두가지 방법을 제공한다.
내포된(Nested) Select: 복잡한 타입을 리턴하는 다른 매핑된 SQL 구문을 실행하는 방법.
내포된(Nested) Results: 조인된 결과물을 반복적으로 사용하여 내포된 결과 매핑을 사용하는 방법.
두 방법의 차이점은 실행할 SQL문에 있다.
1번의 select를 이용하는 방법은 실행할 sql 자체를 간단하게 작성할 수 있는 반면, N+1문제*를 야기할 수 있다.
2번의 result를 이용하는 방법은 Join을 통해서 sql문 자체는 복잡해지지만, 관련된 값들을 바로 매칭시켜줄 수 있다.
* N+1 문제 : 연관 관계가 설정된 엔티티를 조회할 경우에 관련 컬럼을 얻기 위해 쿼리를 N번 추가 수행하는 문제
이 방법은 “N+1 Selects 문제” 으로 알려진 문제점을 가진다. N+1 조회 문제는 처리과정의 특이성으로 인해 야기된다.
- 레코드의 목록을 가져오기 위해 하나의 SQL 구문을 실행한다. (“+1” 에 해당).
- 리턴된 레코드별로 각각의 상세 데이터를 로드하기 위해 select 구문을 실행한다. (“N” 에 해당).
이 문제는 수백 또는 수천의 SQL 구문 실행이라는 결과를 야기할 수 있다. 아마도 언제나 바라는 형태의 처리가 아닐 것이다. 목록을 로드하고 내포된 데이터에 접근하기 위해 즉시 반복적으로 처리한다면 지연로딩으로 호출하고 게다가 성능은 많이 나빠질 것이다.
1. select문을 사용하는 collection
[ 작성 속성 ]
> column : 데이터베이스의 컬럼명 또는 컬럼 별칭명
> property : 결과 컬럼에 매핑하기 위한 필드나 프로퍼티
> javaType : 패키지 경로를 포함한 클래스 전체명이거나 타입 별칭 (매핑할 필드의 자료형과 일치)
> ofType : 자바빈 프로퍼티 타입과 collection의 타입을 구분하기 위해 필요 (컬렉션에 지정된 제네릭 형식과 일치)
> select : collection에 담기는 값 조회할 select문의 id값 => 내포된 select
* 마이바티스 공식 예시 코드
// 2. 실행하고자 하는 select문과 매핑되는 resultMap
<resultMap id="blogResult" type="Blog">
<collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectPostsForBlog"/>
</resultMap>
// 1. 실행하고자 하는 select문
<select id="selectBlog" resultMap="blogResult">
SELECT * FROM BLOG WHERE ID = #{id}
</select>
// 3. resultMap내의 collection과 매핑될 수 있는 select문
<select id="selectPostsForBlog" resultType="Post">
SELECT * FROM POST WHERE BLOG_ID = #{id}
</select>
2. collection내에 컬럼을 직접 작성하는 collection
[ 작성할 속성 ]
> property : 결과 컬럼에 매핑하기 위한 필드나 프로퍼티
> javaType : 패키지 경로를 포함한 클래스 전체명이거나 타입 별칭 (매핑할 필드의 자료형과 일치)
> ofType : 자바빈 프로퍼티 타입과 collection의 타입을 구분하기 위해 필요 (컬렉션에 지정된 제네릭 형식과 일치)
* 마이바티스 공식 예시 코드
<resultMap id="blogResult" type="Blog">
<id property="id" column="blog_id" />
<result property="title" column="blog_title"/>
<collection property="posts" ofType="Post">
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
<result property="body" column="post_body"/>
</collection>
</resultMap>
collection 태그 내에 매핑할 result를 직접 작성해서 연결될 수 있도록 함
- 사용 예시 -
사원이 있고, 사원의 정보에는 사원이 가진 기술이 있다.
사원은 여러개의 기술을 가질 수 있다.
사원을 조회하는 구문에서 다른 사람과 내가 둘 다 collection을 작성했는데, 구문이 다른 것을 보고
마이바티스를 뒤졌다.
* 사원의 정보를 담을 VO에 사원들이 가지는 기술 필드 (List) 작성
public class Staff {
private int staff_no;
private String staff_name;
private String jumin_no;
private String school_name;
private String department_name;
private List<String> skill_name;
private String graduate_day;
}
1. select문을 사용하는 collection
▼ 실행할 SQL문 : 사원 정보를 조회하는 select문
<select id="staffCollectionResultSet" parameterType="_int" resultMap="staffCollectionResultSet">
SELECT
*
FROM
staff
WHERE
STAFF_NO = #{staff_no}
</select>
▼ 해당 구문을 DB에서 실행한 결과값
▼ resultMap내에 collection 작성 : resultMap이 작동 할 때, 결과에 없어도 collection에 해당하는 select문을 실행함
column속성에 '{staff_no=staff_no}'를 작성하는 이유는 해당 resultMap의 컬럼과 전달할 parameter값을 매칭시켜주기 위해
<resultMap id="staffCollectionResultSet" type="staff">
<id column="staff_no" property="staff_no"/>
<result column="STAFF_NAME" property="staff_name" />
<result column="JUMIN_NO" property="jumin_no" />
<result column="DEPARTMENT_NAME" property="department_name" />
<result column="GRADUATE_DAY" property="graduate_day" />
<result column="SCHOOL_NAME" property="school_name" />
<collection property="skill_name" column="{staff_no=staff_no}" ofType="string" javaType="list" select="selectSkillNames">
</collection>
</resultMap>
▼ collection과 매핑되는 select문 : 전달값이 있을 경우, resultMap에 전달받은 parameterType값과 일치하게 작성
<select id="selectSkillNames" parameterType="staff" resultType="String">
select skill_name
from VW_STAFF_INFO
where staff_no = #{staff_no}
</select>
2. collection내에 컬럼을 직접 작성하는 collection
▼실행할 SQL구문 : 사원 정보를 조회하는 select문
select
S.STAFF_NO,
S.STAFF_NAME,
S.JUMIN_NO,
D.DEPARTMENT_NAME,
CC.SCHOOL_NAME,
CS.SKILL_NAME,
S.GRADUATE_DAY
from
staff S
join code_department D on(S.department_code = D.department_code)
left join staff_skill SS on(S.staff_no = SS.staff_no)
left join code_skill CS on(SS.skill_code = CS.skill_code)
join code_school CC on(S.school_code = CC.school_code)
where s.staff_no = #{staff_no}
▼ 해당 구문을 DB에서 실행한 결과값
▼ resultMap내에 작성한 collection과 result
<resultMap id="staffCollectionResultSet" type="staff">
<result column="STAFF_NO" property="staff_no" />
<result column="STAFF_NAME" property="staff_name" />
<result column="JUMIN_NO" property="jumin_no" />
<result column="DEPARTMENT_NAME" property="department_name" />
<result column="GRADUATE_DAY" property="graduate_day" />
<result column="SCHOOL_NAME" property="school_name" />
<collection property="skill_name" ofType="string" javaType="list">
<result column="SKILL_NAME" property="skill_name" />
</collection>
</resultMap>
처음에 collection을 접하고, 오 좋은데 써먹어야지! 하고
얼떨결에 2번 방법으로 작성을 했다.
같은 코드를 1번 방법으로 작성한 사람과 비교를 해서 나도 고쳐봐야지 했는데
왜인지 안돌아가는 것...
결국 1번 코드로 작성한 사람에게 물어보니 실행하는 SQL문부터 달라서 작동이 안되ㅕ던 것을 알았다.
왜?!?!?!?!???? 하다가 공식문서를 뒤져보니 다 나와있긴 하더라...^^
공식문서를 읽어야 한다는 쌤의 가르침을 따르지 못하고 시간을 허비했다고 한다....
'개발 > Framework' 카테고리의 다른 글
[Spring] 스프링에서 AJAX 응답데이터 보내기 (JSON, GSON 사용) / @ResponseBody (0) | 2022.12.27 |
---|---|
[Spring] Spring - 스프링에서 파일 첨부하기 (0) | 2022.12.26 |
[Spring] Spring Legacy Project 디렉토리 구조 (+ 추가 중) (0) | 2022.12.26 |
[Spring] 메이븐(Maven) 개념 / 사용법 (1) | 2022.12.25 |
[MyBatis] MyBatis 동적 SQL 처리 (0) | 2022.12.19 |