230220 Spring Boot 조회 -join N:1, 1:N
조회를 이해하기 위한 조인
조인
조인된 결과가 붙어나오는 2가지 모양
->일대다 조인, 다대일 조인
db에서 조회된 데이터의 결과->java의 객체로 받음
조회된 결과 자체가 매핑을 처리할 때 방향성이 2개.
게시글:회원 = N:1
게시글 left join 회원 on 키=키
게시글(N)에 fk
자바의 객체(VO)로 표현할때도 문제 발생하지 않음
n:1은 무조건 조인
게시글:댓글 = 1:N
게시글 left join 회원 on 키=키
자바의 Object로 나타내기가 어렵다.
ORM작업에 알맞지 않은 모형.
db에서
1번글 1댓글
1번글 2댓글
1번글 3댓글
1번글 4댓글
1번글 5댓글
1번글 6댓글
...
이런식으로 나온다.
때문에, ORM처리를 직접 해줘야 한다.
xml에서
resultMap사용
<resultMap type="" id="">
<result column="" property=""/>
<collection property="" resultMap=""/>
</resultMap>
-type은 리턴의 결과 타입. resultType에 쓰던 것
-id는 resultMap의 이름. select태그의 resultMap=""에 사용.
-column은 데이터베이스의 컬럼값
-property는 처리할 VO의 속성값(getter)
-collection태그의 property는 vo에 있는 list의 변수명
-<result/>태그는 뽑고싶은 값만 넣으면 됨
Test용 sql
CREATE TABLE MEMO(
MNO INT PRIMARY KEY AUTO_INCREMENT,
WRITER VARCHAR(50) NOT NULL,
MEMO VARCHAR(200) NOT NULL
);
insert into MEMO(writer, memo) values('aaa', 'aaa');
insert into MEMO(writer, memo) values('aaa', 'bbb');
CREATE TABLE USERS(
ID VARCHAR(50) PRIMARY KEY,
PW VARCHAR(50) NOT NULL,
NAME VARCHAR(50),
EMAIL VARCHAR(50) UNIQUE
);
insert into users(ID, pw, name, email) values('aaa', '1234', '홍길동', 'aaa@naver.com');
insert into users(ID, pw, name, email) values('bbb', '5678', '이순신', 'bbb@naver.com');
delete from users where id='bbb';
select * from memo;
select * from users;
## N:1
select * from memo m left outer join users u on m.writer=u.id;
## 1:N
select * from users u left outer join memo m on u.id=m.writer;
select m.*, u.name, u.email
from memo m left join users u
on m.writer=u.id
MemoVO.java
package com.simple.basic.command;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class MemoVO {
private int mno;
private String writer;
private String memo;
//N:1 조인에서 N쪽에 처리할 데이터를 추가
private String name;
private String email;
}
UsersVO.java
package com.simple.basic.command;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class UsersVO<T> {
private String id;
private String pw;
private String name;
private String email;
//1:N 조인에서 N쪽을 list로 처리
private List<T> memoList;
}
UsersVO를 만들 때,
list에 여러 타입이 올 수 있을 땐 <T>를 사용하고
만들 때 지정한다.
TestMapper.java
package com.simple.basic.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.simple.basic.command.MemoVO;
import com.simple.basic.command.UsersVO;
@Mapper//스프링 부트에서는 매퍼 인터페이스에 매퍼 어노테이션을 반드시 붙여야 한다.
public interface TestMapper {
public String getTime();
//N:1조인의 모형. ex)글-회원정보
public List<MemoVO> joinOne();
//1:N조인의 모형. ex)회원정보-글
public UsersVO<MemoVO> joinTwo();
}
TestMapper.xml
<?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.simple.basic.mapper.TestMapper">
<select id="getTime" resultType="string">
select now()
</select>
<!-- N:1형식의 조인 -->
<select id="joinOne" resultType="MemoVO">
select m.*,
u.name,
u.email
from memo m left join users u
on m.writer=u.id
</select>
<!-- 1:N형식의 조인 -->
<select id="joinTwo" resultMap="joinResult">
select *
from users u left join memo m
on u.id=m.writer
</select>
<!-- 1:N형식의 조인에서는 ORM작업을 직접 표기해야 합니다. -->
<!-- type에는 리턴의 결과 타입, id는 resultMap의 이름 -->
<!-- column은 데이터베이스의 컬럼값, property는 처리할 VO의 속성값(getter) -->
<resultMap type="UsersVO" id="joinResult">
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="email" property="email"/>
<collection property="memoList" resultMap="memoListResult"/>
</resultMap>
<resultMap type="MemoVO" id="memoListResult">
<result column="mno" property="mno"/>
<result column="writer" property="writer"/>
<result column="memo" property="memo"/>
</resultMap>
</mapper>
select태그와 <collection>태그의 resultMap="" 속성에는 아래 <resultMap>태그의 id와 똑같은 이름을 넣는다.
result의 column속성에는 db의 컬럼값을, property에는 vo의 속성값(getter)을 넣는다.
<collection>태그의 property속성에는 vo에 설정한 list멤버변수의 변수명을 넣는다.
resultMap속성은 새로 지정한 <resultMap>태그의 id를 넣는다.
<collection>태그와 연동될 <resultMap>태그의 type속성은 vo에 설정한 list의 <자료형>을 넣는다.
이 <resultMap>태그에도 <result>태그들이 있다. 마찬가지로 해당 <resultMap>의 type과 연결되는 db의 컬럼값과 vo의 속성값을 넣는다.
JoinTest.java
package com.simple.basic;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.simple.basic.command.MemoVO;
import com.simple.basic.command.UsersVO;
import com.simple.basic.mapper.TestMapper;
@SpringBootTest
public class JoinTest {
@Autowired
private TestMapper testMapper;
//N:1조인
@Test
public void testCode01() {
List<MemoVO> list = testMapper.joinOne();
System.out.println(list.toString());
}
//1:N조인
@Test
public void testCode02() {
UsersVO<MemoVO> vo = testMapper.joinTwo();
System.out.println(vo.toString());
}
}
UserVO<T>자리에 사용할 VO를 넣어준다.
콘솔출력문
1. select m.*,
u.name,
u.email
from memo m left join users u
on m.writer=u.id
|----|-------|-----|-----|--------------|
|mno |writer |memo |name |email |
|----|-------|-----|-----|--------------|
|1 |aaa |bbb |홍길동 |aaa@naver.com |
|2 |aaa |aaa |홍길동 |aaa@naver.com |
|----|-------|-----|-----|--------------|
[MemoVO(mno=1, writer=aaa, memo=bbb, name=홍길동, email=aaa@naver.com), MemoVO(mno=2, writer=aaa, memo=aaa, name=홍길동, email=aaa@naver.com)]
1. select *
from users u left join memo m
on u.id=m.writer
|----|-----|-----|--------------|----|-------|-----|
|id |pw |name |email |mno |writer |memo |
|----|-----|-----|--------------|----|-------|-----|
|aaa |1234 |홍길동 |aaa@naver.com |2 |aaa |aaa |
|aaa |1234 |홍길동 |aaa@naver.com |1 |aaa |bbb |
|----|-----|-----|--------------|----|-------|-----|
UsersVO(id=aaa, pw=null, name=홍길동, email=aaa@naver.com, memoList=[MemoVO(mno=2, writer=aaa, memo=aaa, name=null, email=null), MemoVO(mno=1, writer=aaa, memo=bbb, name=null, email=null)])
mapper.xml에서 resultMap="", <resultMap>, <result>, <collection>들을 사용했기에 자바구문이 정상작동한다.
1:N관계로, memoList에 여러값들이 담긴 것을 확인할 수 있다.
mapper.xml의 코드가 지저분해서, 보통 N:1조인으로 하거나 select를 2번 하게 한다.
+)N:N은 잘못된 조인. 1:N으로 바꾼다.