[Spring Boot] MyBatis와 HSQLDB

개요

여기에서는 Spring Boot + MyBatis + HSQLDB + Thymeleaf를 사용하여 Spring MVC CRUD Web Application을 만드는 방법을 보여준다.

개발환경은 다음과 같다:

● Spring Tool Suite 3 (Version: 3.9.11)

● Spring Boot Version : 2.7.10

● Java : JDK 8


HSQLDB 대신 H2 DB, Apache Derby, SQLite 등의 인메모리 데이터베이스를 사용할 수도 있다.


※ 관련글 목록: http://yellow.kr/lifeView.jsp?s=spring



작성할 웹 애플리케이션 설명

만들려는 예제 Web Application은 책 관리 시스템으로 다음과 같은 기능을 구현한다:

● 모든 책 정보 리스트를 보여준다.

● 새 책 정보 등록

● 책 정보 상세 조회

● 책 정보 수정

● 책 정보 삭제

예제 Application의 Flow는 다음과 같다:



Spring Boot project 생성

● eclips에서 Spring Boot project를 생성한다.

File > New > Project… > Spring Boot > Spring Starter Project

Name, Group, Package에 적당한 내용을 입력하고 [Next] 클릭


● 필요한 Dependency들을 선택한다.

Spring Web, MyBatis, HyperSQL, Thymeleaf를 선택한다.

[Finish] 클릭


● Project 생성 됨

yellow-mybatis 라는 이름으로 Project가 생성되었고, YellowMybatisApplication.java 가 생성되었다.

package com.yellow.mybatis;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class YellowMybatisApplication {

	public static void main(String[] args) {
		SpringApplication.run(YellowMybatisApplication.class, args);
	}

}


예제 애플리케이션이 완성되면 Project Structure은 다음과 같다.



Maven Dependencies

pom.xml을 보면 다음과 같은 dependency를 확인할 수 있다.

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
	<groupId>org.mybatis.spring.boot</groupId>
	<artifactId>mybatis-spring-boot-starter</artifactId>
	<version>2.3.0</version>
</dependency>

<dependency>
	<groupId>org.hsqldb</groupId>
	<artifactId>hsqldb</artifactId>
	<scope>runtime</scope>
</dependency>​



MyBatis

Spring Boot에서 MyBatis를 사용하는 것은 현재 많이 사용되는 방법 중 하나이며, 이는 MyBatis의 강력한 기능과 Spring Boot의 개발 생산성을 결합하여 개발자가 빠르게 애플리케이션을 구축하고 유지 관리할 수 있도록 도와주기 때문이다.

Spring Boot는 MyBatis를 쉽게 통합할 수 있도록 지원하며, MyBatis는 SQL 쿼리 작성 및 실행에 강점을 가지고 있기 때문에 대규모 데이터베이스 애플리케이션에서 효과적으로 사용된다.

Spring Boot에서는 MyBatis를 통합하기 위해 mybatis-spring-boot-starter라는 스타터 종속성을 제공한다. 이 스타터를 추가하면 Spring Boot 애플리케이션에 필요한 MyBatis 라이브러리 및 설정이 자동으로 구성된다. 이러한 기능은 개발자가 보다 쉽고 빠르게 Spring Boot와 MyBatis를 통합하여 사용할 수 있도록 도와준다.

<dependency>
  <groupId>org.mybatis.spring.boot</groupId>
  <artifactId>mybatis-spring-boot-starter</artifactId>
  <version>2.3.0</version>
</dependency>

여기 예제 애플리케이션에서 MyBatis 사용을 위해 application.properties 파일에 MyBatis 설정을 추가해야 한다. 다음과 같이 mybatis.mapper-locations 속성을 사용하여 MyBatis 매퍼 XML 파일의 위치를 지정한다.

mybatis.mapper-locations=classpath:/com/yellow/mybatis/*.xml

mapper XML 파일의 위치는 프로젝트 구조에 따라 유연하게 변경될 수 있다. 주로 src/main/resources 폴더 아래에 위치하며, 일반적으로는 mapper 폴더를 만들어서 그 안에 위치시키는 경우가 많다. 이렇게 하면 mapper XML 파일과 관련된 코드들을 한 곳에 모아서 관리하기 쉽기 때문이다. 여기에서는 Book.xml 하나만 있어 별도의 폴드에 두지 않는다.



HSQLDB

HSQLDB는 Java로 작성된 관계형 데이터베이스 관리 시스템(RDBMS)이다. HSQLDB는 고성능 인메모리 데이터베이스로서, 메모리 상에 데이터를 저장하거나 파일로 저장할 수 있다. HSQLDB는 ACID(Atomicity, Consistency, Isolation, Durability)를 보장하며, SQL 표준을 지원한다. 또한 JDBC 드라이버를 제공하여 Java 언어와의 통합이 용이하다. 다른 비슷한 데이터베이스로 H2 DB, Apache Derby, SQLite 등이 있으며, 사용자의 요구에 맞게 적합한 데이터베이스를 선택할 수 있다.

HSQLDB는 다양한 용도로 사용될 수 있다. 가장 일반적인 용도는 테스트를 위한 인메모리 데이터베이스이다. 테스트를 위한 데이터베이스를 별도로 설치할 필요 없이 HSQLDB를 사용하여 테스트를 수행할 수 있다. 또한 작은 규모의 애플리케이션 개발에 적합하다. HSQLDB는 경량화되어 있으며, 자바 플랫폼에 포함되어 있기 때문에 자바 애플리케이션에 쉽게 통합할 수 있다.

HSQLDB의 장점을 정리하면 다음과 같다:

  • 가벼운 인메모리 데이터베이스: HSQLDB는 인메모리 데이터베이스로 동작할 수 있어서 메모리 사용량이 적다.
  • 빠른 개발: Spring Boot에서 HSQLDB를 사용하면 개발자는 데이터베이스를 설치하거나 관리하지 않아도 된다. 이로 인해 개발 속도가 빨라진다.
  • 테스트 용이성: HSQLDB는 인메모리 데이터베이스로 동작할 수 있기 때문에 테스트를 위한 데이터베이스를 쉽게 구축할 수 있다.
  • 다양한 데이터베이스 지원: HSQLDB는 다양한 데이터베이스를 지원한다. 예를 들어 MySQL과 호환되는 SQL을 지원하며, Oracle과 PostgreSQL과 같은 데이터베이스와의 호환성도 좋다.

Spring Boot에서 인메모리 HSQLDB를 사용할 경우 application.properties 파일에 별도의 HSQLDB 관련 설정이 필요하지 않다.

Spring Boot의 의존성 관리 기능을 이용하여 다음과 같이 HSQLDB 라이브러리를 프로젝트에 추가한다.

<dependency>
	<groupId>org.hsqldb</groupId>
	<artifactId>hsqldb</artifactId>
	<scope>runtime</scope>
</dependency>​



HSQLDB 데이터베이스 초기화

일반적으로 Spring Boot에서 데이터 초기화를 위해 데이터베이스 DDL 및 DML을 실행하려면 schema.sql 또는 data.sql 파일을 사용한다.

schema.sql 파일은 데이터베이스 스키마를 생성하고 수정하는 데 사용된다. 이 파일은 스프링 부트 애플리케이션을 시작할 때 한 번 실행된다.

data.sql 파일은 데이터베이스 초기 데이터를 삽입하는 데 사용된다. 이 파일은 schema.sql이 실행된 후 실행된다.

보통 src/main/resources 폴더 내에 schema.sql 또는 data.sql 파일을 만들어놓으면 스프링 부트가 자동으로 이 파일들을 읽어서 데이터베이스 초기화 작업을 수행한다. 만약 다른 폴더에 위치한 파일을 사용하려면 application.properties에 파일 경로를 지정할 수 있다.


● schema.sql

DROP TABLE book IF EXISTS;

CREATE TABLE book (
  book_id VARCHAR(4),
  title VARCHAR(40),
  author VARCHAR(40),
  publisher VARCHAR(40),
  release_date VARCHAR(8),
  isbn VARCHAR(13),
  PRIMARY KEY(book_id)
);


● data.sql

INSERT INTO book(book_id,title,author,publisher,release_date,isbn) VALUES('1001','장기20세기','조반니 아리기','그린비','20140520','9788976827821');
INSERT INTO book(book_id,title,author,publisher,release_date,isbn) VALUES('1002','신의 지문','그레이엄 핸콕','까치','20170120','9788972916307');
INSERT INTO book(book_id,title,author,publisher,release_date,isbn) VALUES('1003','신화의 이미지','조지프 캠벨','살림출판사','20060220','9788952204776');
INSERT INTO book(book_id,title,author,publisher,release_date,isbn) VALUES('1004','블랙아테나 1','마틴 버낼','소나무','20060110','9788971395479');
INSERT INTO book(book_id,title,author,publisher,release_date,isbn) VALUES('1005','판다의 엄지','스티븐 제이 굴드','사이언스북스','20160520','9788983717788');
INSERT INTO book(book_id,title,author,publisher,release_date,isbn) VALUES('1006','이기적 유전자','리처드 도킨스','을유문화사','20181020','9788932473901');



Model, Repository, Service

● Book.java

package com.yellow.mybatis;

public class Book {
	private String bookId;
	private String title;
	private String author;
	private String publisher;
	private String releaseDate;
	private String isbn;
	
	public Book() {}
	
	public Book(String bookId, String title, String author, String publisher, String releaseDate, String isbn) {
		this.setBookId(bookId);
		this.setTitle(title);
		this.setAuthor(author);
		this.setPublisher(publisher);
		this.setReleaseDate(releaseDate);
		this.setIsbn(isbn);
	}
	
	public String getBookId() {
		return bookId;
	}
	public void setBookId(String bookId) {
		this.bookId = bookId;
	}
	
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	
	public String getAuthor() {
		return author;
	}
	public void setAuthor(String author) {
		this.author = author;
	}
	
	public String getPublisher() {
		return publisher;
	}
	public void setPublisher(String publisher) {
		this.publisher = publisher;
	}
	
	public String getReleaseDate() {
		return releaseDate;
	}
	public void setReleaseDate(String releaseDate) {
		this.releaseDate = releaseDate;
	}

	public String getIsbn() {
		return isbn;
	}
	public void setIsbn(String isbn) {
		this.isbn = isbn;
	}
	
	@Override
	public String toString() {
		return "bookId:" + bookId + ",title:" + title + ",author:" + author 
				+ ",publisher:" + publisher + ",releaseDate:" + releaseDate + ",isbn:" + isbn;
	}
}


● Book.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.yellow.mybatis.BookRepository">
	<select id="findAll" resultType="com.yellow.mybatis.Book">
	SELECT book_id AS bookId, title, author FROM book ORDER BY book_id
	</select>
	<select id="findById" resultType="com.yellow.mybatis.Book">
	SELECT book_id AS bookId, title, author, publisher, release_date AS releaseDate, isbn FROM book 
	WHERE book_id = #{bookId}
	</select>
	<insert id="save" parameterType="com.yellow.mybatis.Book">
	INSERT INTO book(book_id, title, author, publisher, release_date, isbn)
		VALUES(#{bookId},#{title},#{author},#{publisher},#{releaseDate},#{isbn})
	</insert>
	<update id="update" parameterType="com.yellow.mybatis.Book">
	UPDATE book SET title=#{title}, author=#{author}, publisher=#{publisher}, release_date=#{releaseDate}, isbn=#{isbn} 
	WHERE book_id = #{bookId}
	</update>
	<delete id="delete">
	DELETE FROM book 
	WHERE book_id = #{bookId}
	</delete>
</mapper>


● BookRepository.java

package com.yellow.mybatis;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface BookRepository {
	
	public List<Book> findAll();
	public Book findById(String bookId);
	public void save(Book book);
	public void update(Book book);
	public void delete(String bookId);

}


● BookService.java

package com.yellow.mybatis;

import java.util.List;

import org.springframework.stereotype.Service;

@Service
public class BookService {
	
	private final BookRepository bookRepository;
	public BookService(BookRepository bookRepository) {
		this.bookRepository = bookRepository;
	}
	
	public List<Book> getAllBooks() {
		return bookRepository.findAll();
	}
	
	public Book getBookById(String bookId) {
		return bookRepository.findById(bookId);
	}
	
	public void saveBook(Book book) {
		bookRepository.save(book);
	}
	
	public void updateBook(Book book) {
		bookRepository.update(book);
	}
	
	public void deleteBook(String bookId) {
		bookRepository.delete(bookId);
	}

}



Contoller, View(Thymeleaf)

● BookController.java

package com.yellow.mybatis;

import java.util.List;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class BookController {
	
	private BookService bookService;
	
	public BookController(BookService bookService) {
		this.bookService = bookService;
	}
	
	@GetMapping("/")
	public String index() {
		return "index";
	}
	
	@GetMapping("/listBook")
	public String viewBookList(Model model) {
		List<Book> books = bookService.getAllBooks();
		model.addAttribute("allBooks", books);
		
		return "listBook";
	}
	
	@GetMapping("/viewBook/{id}")
	public String viewBook(@PathVariable("id") String id, Model model) {
		Book book = bookService.getBookById(id);
		model.addAttribute("book", book);
		
		return "viewBook";
	}
	
	@GetMapping("/addViewBook")
	public String addViewBook() {
		return "addViewBook";
	}
	
	@PostMapping("/addBook")
	public String addBook(@ModelAttribute Book book) {
		bookService.saveBook(book);
		
		return "redirect:/listBook";
	}
	
	@GetMapping("/updateViewBook/{id}")
	public String updateViewBook(@PathVariable("id") String id, Model model) {
		Book book = bookService.getBookById(id);
		model.addAttribute("book", book);
		
		return "updateViewBook";
	}
	
	@PostMapping("/updateBook/{id}")
	public String updateBook(@PathVariable("id") String id, @ModelAttribute Book book) {
		bookService.updateBook(book);
		
		return "redirect:/listBook";
	}
	
	@GetMapping("/deleteBook/{id}")
	public String deleteBook(@PathVariable("id") String id) {
		bookService.deleteBook(id);
		
		return "redirect:/listBook";
	}

}


● index.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Main Page</title>
</head>
<body>
	<h1>Main Page</h1>      
    <p><a href="/listBook">List Book</a></p>      
    <p><a href="/addViewBook">Register Book</a></p>
</body>
</html>


● listBook.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Book List</title>
</head>
<body>
<h1>Book List</h1>
<br/>
<div>
   <table border="1">
      <tr>
         <th>ID</th>
         <th>제목</th>
         <th>저자</th>
         <th>Action</th>
      </tr>
      <tr th:each ="book : ${allBooks}">
         <td><a th:href="@{/viewBook/{id}(id=${book.bookId})}"  th:text="${book.bookId}"></a></td>
         <td th:text="${book.title}"></td>
         <td th:text="${book.author}"></td>
         <td>
         	<a th:href="@{/updateViewBook/{id}(id=${book.bookId})}" >수정</a>, 
  	        <a th:href="@{/deleteBook/{id}(id=${book.bookId})}" >삭제</a>
         </td>
      </tr>
   </table>
</div>
<br/><br/>
<a href="/">Main Page</a>
</body>
</html>


● viewBook.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>View Book</title>
</head>
<body>
<h1>Book Detail View</h1>
<div>
	<span>Book ID : </span>
	<span th:text="${book.bookId}"></span>
</div>
<div>
	<span>제 목 : </span>
   	<span th:text="${book.title}"></span>
</div>
<div>
   	<span>저 자 : </span>
   	<span th:text="${book.author}"></span>
</div>
<div>
   	<span>출판사 : </span>
   	<span th:text="${book.publisher}"></span>
</div>
<div>
   	<span>출판일 : </span>
	<span th:text="${book.releaseDate}"></span>
</div>
<div>
   	<span>ISBN : </span>
   	<span th:text="${book.isbn}"></span>
</div>
<br>
<br>
<div>
	<a th:href="@{/updateViewBook/{id}(id=${book.bookId})}" >수정</a> /  
  	<a th:href="@{/deleteBook/{id}(id=${book.bookId})}" >삭제</a>
</div>
<p><br></p>
<p><a href="/listBook">List Book</a></p>
</body>
</html>


● addViewBook.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Add a New Book</title>
</head>
<body>
<h1>Add a New Book</h1>
<form method="POST" action="/addBook">
<div>
	<label>Book ID</label>
	<input type="text" id="bookId" name="bookId">
</div>
<div>
	<label>제 목  </label>
	<input type="text" id="title" name="title">
</div>
<div>
	<label>저 자  </label>
	<input type="text" id="author" name="author">
</div>
<div>
	<label>출판사</label>
	<input type="text" id="publisher" name="publisher">
</div>
<div>
	<label>출판일</label>
	<input type="text" id="releaseDate" name="releaseDate">
</div>
<div>
	<label>ISBN </label>
	<input type="text" id="isbn" name="isbn">	
</div>
<div>
	<p><br></p>
	<input type="submit" id="addNew" value="Submit">
</div>
</form>
<p><br></p>
<p><a href="/">Main</a></p>
</body>
</html>


● updateViewBook.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Update a Book</title>
</head>
<body>
<h1>Update a Book</h1>
<form th:action="@{/updateBook/{id}(id=${book.bookId})}" method="POST" >
<div>
	<label>Book ID :</label>
	<span th:text="${book.bookId}"></span>
	<input type="hidden" th:field="*{book.bookId}" >
</div>
<div>
	<label>제 목  </label>
	<input type="text" th:field="*{book.title}" >
</div>
<div>
	<label>저 자  </label>
	<input type="text" th:field="*{book.author}" >
</div>
<div>
	<label>출판사</label>
	<input type="text" th:field="*{book.publisher}" >
</div>
<div>
	<label>출판일</label>
	<input type="text" th:field="*{book.releaseDate}" >
</div>
<div>
	<label>ISBN </label>
	<input type="text" th:field="*{book.isbn}" >	
</div>
<div>
	<p><br></p>
	<input type="submit" id="addNew" value="Submit">
</div>
</form>
<p><br></p>
<p><a href="/">Main</a></p>
</body>
</html>



Application 실행

Project를 선택한 후, Run AS > Spring Boot App 를 하면 내장 Tomcat 서버에 배포된다. 브라우저에서 다음의 URL을 실행한다.​​

● http://localhost:8080/

● http://localhost:8080/listBook

● http://localhost:8080/addViewBook

● http://localhost:8080/listBook

● http://localhost:8080/viewBook/1004

● http://localhost:8080/updateViewBook/1004

● http://localhost:8080/listBook

​등등…

[Spring Boot] MyBatis와 HSQLDB
Tagged on:                     

One thought on “[Spring Boot] MyBatis와 HSQLDB

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.