냐냐한 IT/냐냐한 Spring Boot

MyBatis - Join 매핑하기

소소하냐 2019. 9. 27. 17:05

Spring Boot + MyBatis 프로젝트 연습 목록 

- 01. 신규 Spring Boot 프로젝트 만들기

- 02. Thymeleaf, spring-boot-devtools 추가

- 03. Spring Boot에 H2 추가 

- 04. Spring Boot + H2 + Jdbc로 사용해보기

- 05. Spring Boot + H2 + MyBatis로 사용해보기 

- 06. MyBatis Logging 추가  

- 07. MyBatis - Join 매핑하기 (현재 포스트)


거의 대부분의 개발 시, 각 테이블들 간에는 여러 관계들로 엮여있습니다. 그에 따라 join으로 select를 할 때 어떻게 데이터를 매핑하여 가져오는지 해보기로 했습니다. 예전에 잠시 사용할 때에는 join 해서 가져올 데이터에 대해 VO(Value Object)를 만들어 매핑되도록 사용했습니다. 하지만 객체 중심의 개발을 위해 resultMap을 이용하여 매핑하는 방법을 연습해 보기로 했습니다. 

 

 

시작 전 구현 및 리팩토링 내용 

DiaryController 구현

1. Diary와 Account 테이블을 관계 맺고 제대로 동작하는지 화면 확인을 위해 DiaryController와 list/view/add/modify.html을 구현

2. 데이터베이스에는 snake_case를, Java 코드에서는 carmelCase로설정을 하였기에 이름이 일치하지 않아 데이터를 제대로 가져오지 못하는 문제가 발생

snake_case(diary_date)를 carmelCase(diaryDate)로 변경되도록 설정 ( mybatis-config.xml

: MyBatis 공식 - Auto-mapping 내용 보기 [새창]

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<settings>
		<setting name="mapUnderscoreToCamelCase" value="true"/>
	</settings>
	<!-- ...생략... -->
</configuration>

- git : add diaryController, html, mapUnderscoreToCamelCase 변경 내용 보기 [새창] 

리팩토링 : 테이블명 accounts -> account로 변경

schema.sql, AccountMapper.xml 에서 accounts를 account로 변경

 

- git : refactoring table name: accounts -> account 변경 내용 보기 [새창]

 

 

MyBatis - Join 매핑하기 

 

1. account_id 필드를 Diary에 추가 (schema.sql)

CREATE TABLE diary (
  id BIGINT AUTO_INCREMENT  PRIMARY KEY,
  diary_date CHAR(8) NOT NULL,
  contents VARCHAR(500) NOT NULL,
  account_id BIGINT NOT NULL,
  FOREIGN KEY (account_id) REFERENCES account(id) ON DELETE CASCADE
);

INSERT INTO diary (diary_date, contents, account_id) VALUES 
  ('20190910', 'contents1_by1', 1),
  ('20190911', 'contents2_by1', 1),
  ('20190911', 'contents3_by1', 1),
  ('20190913', 'contents4_by2', 2),
  ('20190913', 'contents5_by2', 2);

 

2. Diary.java 에 Account 추가 및 그에 따른 생성자 및 toString수정

public class Diary {
  //...생략...	
	private Account account;
	
	public Account getAccount() {
		return account;
	}
	
	public void setAccount(Account account) {
		this.account = account;
	}
  
  //Account account 인자 추가 
  public Diary(String diaryDate, String contents, Account account) {
		this.diaryDate = diaryDate;
		this.contents = contents;
		this.account = account;
	}
  //...생략...
}

 

3. Diary 클래스의 생성자 변경에 따라 MybatisDiaryRepositoryTest.java 수정 (소스는 아래 gitHub에서 확인) 

 

4. DiaryMapper.xml 수정 

    - insert 쿼리에 추가한 account_id 추가

    - join으로 account 정보도 함께 가져오도록 쿼리 수정 + resultMap으로 매핑 처리 

<mapper namespace="Diary">
  <resultMap type="Diary" id="diaryIncludeAccountMap">
		<id property="id" column="id" />
		<result property="diaryDate" column="diary_date"/>
		<result property="contents" column="contents" />
		<association property="account" column="account_id" resultMap="accountMap" />
	</resultMap>
	
	<resultMap type="Account" id="accountMap">
		<id property="id" column="id" />
		<result property="email" column="email" />
	</resultMap> 
  
 	<!--...생략...	-->
  
 	<insert id="save" parameterType="Diary" useGeneratedKeys="true" keyProperty="id">
 		insert into diary (diary_date, contents, account_id) 
 		values (#{diaryDate}, #{contents}, #{account.id})
 	</insert>
  
 	<select id="findAll" resultMap="diaryIncludeAccountMap">
 		select * 
 		from diary d
 			inner join account a on d.account_id = a.id  
 	</select>
 	
 	<select id="findById" parameterType="long" resultMap="diaryIncludeAccountMap">
 		select * 
 		from diary d
 			inner join account a on d.account_id = a.id  
 		where d.id = #{id}
 	</select>
</mapper>

 

5. 단위 테스트 실행시 오류가 발생하였다. 오류 내용은 아래와 같다 

: org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.executor.ExecutorException: No constructor found in com.sosohanya.leveldiary.account.Account matching [java.lang.Long, java.lang.String, java.lang.String, java.lang.Long, java.lang.Long, java.lang.String]

    - 오류문구에 있는 데로 반환 칼럼을 모두 포함하는 생성자를 정의하는 것이 아니라 기본 생성자를 정의하면 해결된다. 

//Account.java 소스 중 일부
public class Account {
  public Account() { }
}

//Diary.java 소스 중 일부
public class Diary {
  public Diary() { }
}

 

6. DiaryController 및 View 페이지 수정 

    - 추가시(insert) Account도 등록되도록 추가 

	@PostMapping("/add") //추가 처리 
	public String addProcess(Diary diary){
		diary.setAccount(new Account("controller@email.com")); //차후 로그인한 유저로 바뀌겠지만 우선은 임시
		diaryRepository.save(diary);
		
		return "redirect:/diary/list";
	}

    - list.html / view.html에 account email 표시하도록 수정

<!-- list.html 소스 중 일부 -->
<td th:text=${diary.account.id}></td>
<td th:text=${diary.account.email}></td>

<!-- view.html 소스 중 일부 -->
<span th:text="${diary.account.email}"></span>

 

지금까지의 내용은 github에 branch:007-join-mapping-using-resultMap[새창]으로 확인하실 수 있습니다

 

 

참고 사이트 : 

MyBatis 공식 - Result Maps 내용 보기 [새창]

MyBatis를 ORM처럼 사용하기 [새창] 

 

 

 


Spring Boot + MyBatis 프로젝트 연습 목록 

- 01. 신규 Spring Boot 프로젝트 만들기

- 02. Thymeleaf, spring-boot-devtools 추가

- 03. Spring Boot에 H2 추가 

- 04. Spring Boot + H2 + Jdbc로 사용해보기

- 05. Spring Boot + H2 + MyBatis로 사용해보기 

- 06. MyBatis Logging 추가  

- 07. MyBatis - Join 매핑하기 (현재 포스트)