java를 이용해 객체 내 List로 데이터를 받아와야 하는 상황이 생겨 온갖 삽질을 했기에 기록해두려 한다.
sql은 객체내에 리스트를 만들어 결과값을 받아올 수 없기때문에 조회 시 아래처럼 메모는 모두 출력되나 고객에 대한 정보가 중복해서 출력된다.
아래 사진은 임시값으로 riId값을 고객정보라고 생각하면 된다. 아래 두 메모 모두 1번 고객에게 작성된 메모이다.
이럴때는 mybatis에서 제공하는 resultMap
과 collection
을 이용하면 된다.
고객테이블
- u_id
- u_name
- u_phone
- u_loginId
- c_id // 회사 id. 회사와 고객은 고객과 메모처럼 1:N 관계를 갖지만 여기서 중요한 부분은 아님.
메모테이블
- m_id
- u_id // 고객id
- m_memo // 메모내용
- m_insdate // 작성일자
@Data
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class UserCsDto {
// camalcase를 적용함
private Long uId;
private String cName; // 회사이름
private String uPhone;
private String uName;
private String uLoginId;
private List<UserCsMemoDto> memoList;
}
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@Builder
public class UserCsMemoDto {
private String memo;
}
@Repository
@Mapper
public interface UserMapper {
List<UserCsResDto> findUserAndMemo(Long id);
}
<!-- 메모 조회 확인용 -->
<resultMap id="userMemoInfo" type="UserCsResDto">
<id property="uId" column="u_id"/>
<result property="uName" column="u_name"/>
<result property="uPhone" column="u_phone"/>
<result property="groupName" column="c_name"/>
<collection property="memoList" column="uid = u_id" ofType="UserCsMemoDto" select="findMemo"/>
</resultMap>
<!-- 유저 정보 가져옴 -->
<select id="findUserAndMemo" parameterType="java.lang.Long" resultMap="userMemoInfo">
SELECT u.u_id, u.u_name, u.u_birthdate, u.u_phone, c.c_name, u.u_loginId
FROM user_info u
JOIN compony_info c
ON u.c_id = c.c_id
WHERE u.u_id = #{id}
</select>
<!-- 메모 가져옴 -->
<select id="findMemo" resultType="UserCsMemoDto">
SELECT m_memo AS memo,
u_id
FROM user_cs_memo
WHERE u_id = #{uid}
ORDER BY m_ins_time DESC // 메모 작성시간의 역순으로 정렬
</select>
<!-- 메모조회 확인 끝 -->
기본적인 사용법은
1) 메인쿼리 작성. 메인쿼리의 id로 mapper interface에 선언하면 됨. 메인쿼리의 resultMap값을 아래 2)의 resultMap의 id와 맞춰준다.
2) resultMap 작성. id
는 중복을 구분할 pk값. result
는 메인쿼리에서 조회한 컬럼을 가져오는 역할을 한다.
collection
은 1:N 관계를 가질 때 적용할 서브쿼리와 연결해주는 역할을 한다.
collection
의 select값과 서브쿼리의 id값을 동일하게 해 준다.
3) 서브쿼리 작성. 이번처럼 메인쿼리로부터 조건값을 받아와야 할 경우 collection
에 가져올 변수의 이름과 값을 선언해주고, 해당 변수의 이름을 서브쿼리에서 가져와 사용한다.
동작순서: 메인쿼리 -> resultMap -> id, result -> collection -> 서브쿼리 -> resultMap 종료 후 선언한 type형으로 결과반환
참고로 collection은 id, result속성의 뒤에 위치해야 한다.
이때 collection
에 들어가는 column="uid = u_id"
는 메인컬럼에서 가져온 u_id
컬럼의 값을 서브쿼리의 #{uid}
에 넣어준다고 생각하면 된다.
그리고 컬럼의 값을 가져와 column
에서 사용할때 resultMap
의 property
에서 지정해준 별칭은 적용되지 않고, 메인쿼리의 컬럼값을 그대로 사용해야하는 것 같다.
아래는 위의 예시로 조회한 결과.
한명의 고객에게 2개의 메모가 저장되어있는 상황으로, 정상적으로 조회되었다.
[
{
"uId": 1,
"groupName": "회사이름예시",
"uPhone": "01011112222",
"uName": "홍길동",
"uLoginId": aaabbbccc,
"memoList": [
{
"memo": "testmemo"
},
{
"memo": "메모수정한내용메모수정한내용메모수정한내용메모수정한내용메모수정한내용메모수정한내용메모수정한내용"
}
]
}
]