09장 O/R 매퍼 알아보기

박준우·2025년 7월 8일
0

Spring Boot

목록 보기
9/14

1. MyBatis 알아보기

(1) O/R 매퍼란?

OR 매퍼란? 애플리케이션에서ㅕ 다루는 객체 O:(Object)와, R:(Relation Database) 관계형 DB의 데이터를 매핑하는 것을 의미한다. OR매퍼는 미리설정된 객체와 DB에 값을 가져오거나, 대입하는 등의 작업을 자동으로 수행한다.

(2) myBatis란?

마이바티스는 OR매퍼기능을 지원하는 무료 오픈소스 소프트웨어로 SQL을 (XML, 자바 에너테이션)을 이용해 직접 작성할 수 있다.

my Batis 사용법

방법1: 자바 인터페이스에 에너테이션을 사용해 SQL 직접 작성하기

public interface BookMapper {
    @Select("SELECT * FROM books WHERE id = #{id}")
    Book getBookById(int id);
}

방법2: 매퍼 파일 사용하기
매퍼파일이란? XML을 이용하여, SQL과 그 결과를 자바객체에 매핑하는 방법을 정의한 파일이다.

<mapper namespace="com.example.BookMapper">
    <select id="getBookById" parameterType="int" resultType="com.example.Book">
        SELECT * FROM books WHERE id = #{id}
    </select>
</mapper>

2. MyBatis 사용해보기

(0) 사전 프로그램 세팅

의존성 추가
1. Spring Boot DevTools
2. Thymeleaf
3. Spring Web
4. Lombok
5. H2 Database (자바 기반 DB로 가벼워서, 개발 및 테스트용으로 사용한다.)
6. MyBatis Framework (spring boot 버전 3.4.7로 하향 필요)

빌드 세팅
1. 설정-빌드도구-gradle-gradle jvm에서 java버전 변경하기.
2. 프로젝트 구조- 프로젝트설정-프로젝트SDK에서 java버전 변경하기.

(1) application.properties란?

src -> main-> resource 안에 들어 있는 파일로, 애플리케이션의 설정과 속성을 정의하는 파일이다. 즈로, 키-값의 형식으로 작성되며, 각 환경(개발, 테스트, 프로덕션 등)마다 다른 application.properties를 둘 수 있어, 이를 통해 환경별 설정이 가능하다.

# 데이터베이스 접속 URL
# 여기서는 H2 데이터베이스의 메모리 모드를 사용
spring.datasource.url=jdbc:h2:mem:testdb

# 데이터베이스의 드라이버 클래스
# 여기서는 H2 데이터베이스의 드라이버를 지정
spring.datasource.driver-class-name=org.h2.Driver

# 데이터베이스 접속 시 사용자 이름
spring.datasource.username=sa

# 데이터베이스 접속 시 비밀번호. 여기서는 비밀번호를 설정하지 않음.
spring.datasource.password=

# H2 데이터베이스의 콘솔을 활성화하기 위한 설정
# 이를 통해 브라우저에서 H2 콘솔에 접근할 수 있음
spring.h2.console.enabled=true

#### 해석
H2 데이터베이스를 사용하기 위해 위와 같은, 설정을 하였다.

spring.datasource.url: 데이터 베이스의 연결 URL을 지정하는 키
jdbc:h2:mem:testdb : JDBC를 이용해 H2 데이터베이스에 연결하기 위한 URL
jdvc : 자바 애플리케이션이 DB에 연결하기 위한 프로토콜
h2 : 사용할 DB의 종류
mem : H2DB의 메모리 모드 작동, 이 설정은 DB가 메모리 내에 존재하고, 애플리케이션이 종료되면 데이터가 사라진다.
(디스크 모드면 영구 저장)
testdb : 메모리내 DB이름으로, 사용자가 지정한다.

(2) schema.sql과 data.sql

스프링 부트에서는 특정 이름의 SQL 파일을 src/main/resource 폴더 아래에 배치하면, 애플리케이션 시작시 자동으로 DB를 초기화 및 데이터 삽입을 할 수 있다.
단, H2 DB에서만 자동 초기화, 데이터삽입을 할 수 있기 때문에, 다른 DB를 사용한다면 이 기능을 직접 활성화 시켜 줘야한다.

schema.sql

이 파일에는 테이블 생성, 인덱스 정의, 외래키 설정 등의 DB 구조와 관련된 SQL을 작성하며, 애플리케이션 시작시 자동으로 DB의 구조가 정의된다.

스키마 삽입

CREATE TABLE books (
	-- 기본키로서의 ID: 자동 증가
	id INT PRIMARY KEY AUTO_INCREMENT,
	-- 책 제목: NULL을 허용하지 않음
	title VARCHAR(255) NOT NULL,
	-- 책 저자명: NULL을 허용하지 않음
	author VARCHAR(255) NOT NULL
);

data.sql

테이블에 데이터 등록 등 초기 데이터 설정과 관련된 SQL문장을 작성한다.

데이터 삽입

INSERT INTO books (title, author) VALUES ('스프링 부트 핵심 가이드', '장정우');
INSERT INTO books (title, author) VALUES ('그림으로 쉽게 이해하는 웹/HTTP/네트워크', '임지영');
INSERT INTO books (title, author) VALUES ('엘라스틱서치 바이블', '여동현');

(3) H2 콘솔 실행

프로젝트를 실행하려면, localhost:8080/h2-console에 들어가면, H2콘솔화면이 표시된다. 이를 이용해 브라우저에서 직접 H2DB에 접속해 SQL을 실행하거나, DB의 내용을 확인할 수 있다.

H2 콘솔 항목은 다음과 같다.
1. 저장한 설정: 이전에 저장된 연결설정이 저장되어 있다.
2. 설정이름: 현재 연결 설정의 이름
3. 드라이버 클래스: 사용할 jdbc 드라이버의 클래스 이름이 저장된다.
4. jdbcURL : DB연결 URL을 지정하며, DB의 위치와 연결방법이 저장되어있음.
5. 사용자명: 접속시 사용자 이름을 입력(기본은 sa)
6. 비밀번호: h2db의 기본 비밀번호, (기본은 비어있다.)

연결 후 왼쪽의 BOOKS테이블을 들어가면 아래와 같은 결과가 나타난다.

(4) 테이블 생성 규칙

MyBatis로 테이블(엔티티)을 생성할 때 지켜야할 규칙이 있다.

  1. 엔터티의 이름은 임의지만, 일반적으로 테이블 이름이나, 그 내용을 반영해야한다.
  2. 엔티티의 객체를 생성할 때 기본 생성자를 사용하기 때문에, 인수 없는 생성자가 필수로 있어야한다.
  3. 엔터티의 필드(열) 이름은 대상 테이블의 칼럼명과 일치해야한다.
  4. getter, setter 메소드가 반드시 필요하다.

테이블 생성하기

@Data
public class Book {
    /** 도서 ID */
    private int id;
    /** 제목 */
    private String title;
    /** 저자 */
    private String author;
}

#### 해석
위 테이블은, 테이블 생성규칙을 모두 지키는 예다.
1. Book을 사용해, schema.sql 에서 사용한, BOOKS 테이블의 내용을 반영한다.
2. 기본생성자는 @Data가 만들어준다.
3. 클래스의 필드명인, id, title author는 BOOKS 테이블의 속성과 일치한다.
4. getter, setter는 @Data에 의해 자동으로 만들어진다.

(5) 매퍼 파일 생성

이번 예제에서는 에너테이션이 아닌 Mapper 파일(XML)을 사용해, O/R Mapping을 구현한다.

매퍼 파일을 사용하려면 두가지를 생성해 줘야한다.
1. 매퍼 인터페이스 생성 : 엔터티와 대응하는 매퍼 인터페이스를 만든다.
2. 매퍼 파일 생성 : 매퍼 인터페이스에 해당하는 XML파일을 생성한다. 이 파일은 SQL과 자바 메서드의 매핑을 정의한다.

또한, 매퍼를 추가할 때, 패키지 추가가 아닌, 경로추가를 해준다면, IDE가 경로를 더 잘 찾는다.

매퍼 인터페이스 생성

@Mapper
public interface BookMapper {
	/** 모든 책을 가져옴 */
	List<Book> getAllBooks();
	/** id로 1건을 조회 */
	Book getBookById(int id);
	/** 등록 */
	void insertBook(Book book);
	/** 업데이트 */
	void updateBook(Book book);
	/** 삭제 */
	void deleteBookById(int id);
}
#### 해석
@Mapper : 이 자바의 인터페이스가 매퍼임을 나타낸다. 또한, 자동으로 인터페이스 구현부를 생성하고 
SQL 작업 수행을 위한 메소드를 만들어준다. 

mybatis 플러그인 설치

파일->설정 에 들어가면, 플러그인 설치가 가능하다. 여기서 myBatisX를 설치한다.
그후 아래 매퍼인터페이스명.xml(BookMapper.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.example.demo.mapper.BookMapper">
	<!-- 【SELECT】 모든 책을 가져오기 위한 SQL을 정의 -->
	<select id="getAllBooks" resultType="com.example.demo.entity.Book">
		SELECT id, title, author FROM books ORDER BY id
	</select>
	
	<!-- 【SELECT】 특정 ID를 가진 책을 가져오기 위한 SQL을 정의 -->
	<select id="getBookById" resultType="com.example.demo.entity.Book">
		SELECT id, title, author FROM books WHERE id = #{id}
	</select>
	
	<!-- 【INSERT】 새로운 책을 데이터베이스에 추가하는 SQL을 정의 -->
	<insert id="insertBook" parameterType="com.example.demo.entity.Book">
		INSERT INTO books (title, author) VALUES (#{title}, #{author})
	</insert>
	
	<!-- 【UPDATE】 특정 ID를 가진 책의 정보를 업데이트하는 SQL을 정의 -->
	<update id="updateBook" parameterType="com.example.demo.entity.Book">
		UPDATE books SET title = #{title}, author = #{author}
		WHERE id = #{id}
	</update>
	
	<!-- 【DELETE】 특정 ID를 가진 책의 정보를 삭제하는 SQL을 정의 -->
	<delete id="deleteBookById" parameterType="int">
		DELETE FROM books WHERE id = #{id}
	</delete>
</mapper>

#### 해석
1. 선언부
<?xml version="1.0" encoding="UTF-8"?> : 이 파일을 xml파일로 지정한다. 

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> : 이 파일이 mapper DTD규칙을 지킴을 선언한다.
DTD란 XML사용시 반드시 있어야 하는 구조정의다. 

<mapper namespace="com.example.demo.mapper.BookMapper"> : 이 파일이
지정한 위치의 자바 인터페이스와 연결됨을 의미한다.

2. 쿼리
<select id="getAllBooks" resultType="com.example.demo.entity.Book">
	SELECT id, title, author FROM books ORDER BY id
</select> 

select : select 기능을 구현한다.
id="getAllBooks": 매퍼 인터페이스의 getAllBooks를 구현한다. 
resultType="com.example.demo.entity.Book" : 쿼리 결과가 이 주소로 매핑된다.
SELECT id, title, author FROM books ORDER BY id : 실제 실행되는 SQL

parameterType="com.example.demo.entity.Book" : 생성된 테이블에서 Book 객체를 인자로 받는다. 
select를 제외한, 나머지에서 사용해야하며, SQL에 전달되는 자바 타입을 FQCN(전체 경로)로 지정한다. 

#{id } : 이는 플레이스 홀더라고 부르며, 특정값으로 대체될 예약된 위치를 의미한다.

(6) 컨트롤러 만들기

@Controller
@RequiredArgsConstructor
public class BookController {
	// DI
	private final BookMapper bookMapper;

	// 메뉴 화면 표시, 
	@GetMapping("/") /URL입력시
	public String showIndex() {
		return "book/index"; //html 파일 출력
	}

	// 모든 책 보기
	@GetMapping("/list")
	public String showAllBooks(Model model) {
		model.addAttribute("message", "목록 보기");
		model.addAttribute("books", bookMapper.getAllBooks());
		return "book/success";
	}
    
    //list URL로 접근시, Model에 message, books 모델을 추가하고, 이를
    book/success html로 리턴한다. 

	// 특정 ID를 가진 책 가져오기
	@GetMapping("/detail/{id}")
	public String showBook(@PathVariable int id, Model model) {
		model.addAttribute("message", "상세 보기");
		model.addAttribute("book", bookMapper.getBookById(id));
		return "book/success";
	}
    
	// detail URL 실행전에, {id}를 @PathVariable로 미리 받아온다. 
   
	// 새 책 만들기
	@GetMapping("/create")
	public String createBook(Model model) {
		// 등록용 더미 데이터
		Book book = new Book();
		book.setTitle("자바/스프링 개발자를 위한 실용주의 프로그래밍");
		book.setAuthor("김우근");
		bookMapper.insertBook(book);
		model.addAttribute("message", "등록 성공");
		return "book/success";
	}
    
    

	// 특정 ID를 가진 책 업데이트
	@GetMapping("/update/{id}")
	public String updateBook(@PathVariable int id, Model model) {
		// 업데이트용 더미 데이터
		Book book = new Book();
		book.setId(id);
		book.setTitle("업데이트된 제목");
		book.setAuthor("업데이트된 저자");
		bookMapper.updateBook(book);
		model.addAttribute("message", "업데이트 성공");
		return "book/success";
	}

	// 특정 ID를 가진 책 삭제
	@GetMapping("/delete/{id}")
	public String deleteBook(@PathVariable int id, Model model) {
		bookMapper.deleteBookById(id);
		model.addAttribute("message", "삭제 성공");
		return "book/success";
	}
}

#### 해석
@RequiredArgsConstructor : Lombok 에노테이션으로, private final로 선언된
bookMapper 객체에 자동으로 생성자를 주입해 DB와 연결한다.

(7) 입력, 출력 뷰 만들기

입력 뷰

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
	<meta charset="UTF-8">
	<title>메뉴 화면</title>
</head>
<body>
	<h1>도서 관리 메뉴ー</h1>
	<ul>
		<li><a th:href="@{/list}">도서 목록</a></li>
		<li><a th:href="@{/detail/2}">도서 상세 정보</a></li>
		<li><a th:href="@{/create}">도서 등록</a></li>
		<li><a th:href="@{/update/2}">도서 수정</a></li>
		<li><a th:href="@{/delete/2}">도서 삭제</a></li>
	</ul>
</body>
</html>

### 해석
3개는 고정된, 파라미터로 2를 전달한다.

출력 뷰

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
	<meta charset="UTF-8">
	<title>작업 결과</title>
</head>
<body>
	<h1 th:text="${message}">메시지</h1>
	<!-- 도서 목록의 경우 -->
	<table th:if="${books}" border="1"> // 테이블의 두께를 1로 지정
		<thead>
			<tr>
				<th>ID</th>
				<th>제목</th>
				<th>저자</th>
			</tr>
		</thead>
		<tbody>
			<tr th:each="book : ${books}">
				<td th:text="${book.id}">ID</td>
				<td th:text="${book.title}">제목</td>
				<td th:text="${book.author}">저자</td>
			</tr>
		</tbody>
	</table>
	<!-- 도서 상세 정보의 경우 -->
	<div th:if="${book}">
		<p>
			<strong>ID: </strong>
			<span th:text="${book.id}">ID</span>
		</p>
		<p>
			<strong>제목: </strong>
			<span th:text="${book.title}">제목</span>
		</p>
		<p>
			<strong>저자: </strong>
			<span th:text="${book.author}">저자</span>
		</p>
	</div>
	<a th:href="@{/}">메뉴로 돌아가기</a>
</body>
</html>


결과

(참조: 마이 바티스 프레임 워크가 더이상 작동하지 않아 현 시점에서는 다른 방법을 찾아야한다.) 이를 추후에 업로드 하겠다.

profile
DB가 좋아요

0개의 댓글