[Tutorial] JPA - CRUD RestController 만들기 [2/3]

이번에는 JPA를 쉽고, 편하게 구현하기 위해 Spring Data JPA를 활용해보았습니다.

JPA를 사용하기에 앞서, Object Relation Mapping에 대해 살펴보면 좋을 것 같습니다.

Spring Data JPA를 구현하면서 아래의 사이트를 참조했습니다.

예제들을 공부하기에 좋은 사이트입니다.

간단한 Book Table을 기준으로, CRUD RestController를 만들어보겠습니다.

Class: BookEntity
Field: 
  UUID      id
  String    name
  String    category
  Long      sellCount
  TimeStamp createAt

개발 순서는 아래와 같이 진행했습니다.

  1. 프로젝트 생성
  2. Database connection
  3. Entity 생성
  4. Repository 구현
  5. Controller 구현

프로젝트 생성

Spring Initializr를 통해 example-book프로젝트를 생성합니다.
아래 라이브러리들을 추가했습니다.

  • Spring Web
  • Spring Data JPA
  • H2 Database

Database Connection

h2 database는 in-memory 방식으로 간편하게 사용할 수 있습니다.

/resources/application.properties 설정입니다.

spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
  
spring.h2.console.enabled=true

참조1: https://www.baeldung.com/spring-boot-h2-database

참조2: https://www.baeldung.com/spring-boot-access-h2-database-multiple-apps

SQL 보기 및 정렬, h2 database의 콘솔 화면을 볼 수 있는 옵션을 추가했습니다.

서버 구동 후 http://localhost:8080/h2-console로 접속해 연결이 잘 되는지 테스트합니다.

스크린샷 2020-02-17 오후 4 53 27

잘 연결되었네요.

BookEntity 생성

package com.mz.example.examplebook.domain.book;  
  
import org.hibernate.annotations.CreationTimestamp;  
import org.hibernate.annotations.Type;  
  
import javax.persistence.*;  
import java.util.Date;  
import java.util.UUID;  
  
@Entity  
@Table(name="book")  
public class BookEntity {  
  
  @Id  
  @Type(type = "uuid-char")  
  @GeneratedValue  
  private UUID id;  
  
  @Column(nullable = false)  
  private String name;  
  
  @Column(nullable = false)  
  private String category;  
  
  @Column(nullable = false)  
  private long sellCount;  
  
  @Column(nullable = false, updatable = false)  
  @Temporal(TemporalType.TIMESTAMP)  
  @CreationTimestamp  
  private Date createAt;

  // default, all args constructor...
  // getter, setter
  // toString...
}

참조: https://www.baeldung.com/spring-boot-hibernate

@GeneratedValue ID생성 방식에 대한 정의로, Id 필드의 타입이 UUID일 경우 자동으로 uuid를 생성해 값을 넣어줍니다.

참조: https://www.baeldung.com/hibernate-identifiers

서버 재구동 후 로그를 확인했습니다.
Book 테이블이 생성되어있습니다.

스크린샷 2020-02-17 오후 1 44 12

BookRepository 생성

package com.mz.example.examplebook.domain.book;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import java.util.UUID;

@Repository
public interface BookRepository extends CrudRepository<BookEntity, UUID> {

}

CrudRepository를 상속 받은 Repository를 구현합니다.
기본적인 CRUD 구현은 끝났습니다.

참조1: https://www.baeldung.com/spring-boot-hibernate

참조2: CrudRepositoryJpaRepository

BookController 생성

package com.mz.example.examplebook.domain.book;  
  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.web.bind.annotation.*;  
  
import java.util.ArrayList;  
import java.util.List;  
import java.util.UUID;  
  
@RestController()  
public class BookController {  
  
  @Autowired  
  private BookRepository bookRepository;  
  
  @PostMapping("/book")  
  public BookEntity createBook(@RequestBody BookEntity bookEntity) {  
    BookEntity created = bookRepository.save(bookEntity);  
    return created;  
  }  
  
  @GetMapping("/book")  
  public List<BookEntity> listAllBooks() {  
    List<BookEntity> list = new ArrayList<>();  
    Iterable<BookEntity> iterable = bookRepository.findAll();  
    for (BookEntity bookEntity : iterable) {  
      list.add(bookEntity);  
    }  
    return list;  
  }  
  
  @PutMapping("/book/{bookId}")  
  public BookEntity updateBook(@PathVariable("bookId") UUID bookId,  
                                 @RequestBody BookEntity bookEntity) {  
      bookEntity.setId(bookId);  
      BookEntity updated = bookRepository.save(bookEntity);  
      return updated;  
  }  
  
  @DeleteMapping("/book/{bookId}")  
  public void deleteBook(@PathVariable("bookId") UUID bookId) {  
    bookRepository.deleteById(bookId);  
  }  
}

data.sql

/resources/data.sql 파일을 생성해보았습니다.
테스트용 초기 데이터를 추가해두었습니다.
해당 위치에 있으면 Spring JDBC가 자동으로 이를 인식하고 sql을 실행해줍니다.

INSERT INTO book (id, name, category, sell_count, create_at) VALUES
     ('7c9649e3-944a-4bfc-a332-e77c3ce517af', '열 번의 산책', '인문', 9000, NOW()),
     ('c164e06d-a57c-4045-a03d-43ce3b33d09a', '숲길', '인문', 12000, NOW()),
     ('f60dfe05-ba02-4f18-b663-eb0f0d2be4c9', '바닷마을 인문학', '인문', 13500, NOW()),
     ('bf4f57d6-f2bd-4c04-9138-2d214a943019', '트렌드 코리아 2020', '경제', 25400, NOW()),
     ('0918b305-093c-4d83-bcdf-49ec86a701ed', '부의 인문학', '경제', 14500, NOW()),
     ('faaacd46-240a-434c-a210-a3de0d6d9292', '넛지', '경제', 13500, NOW()),
     ('d02f77b1-51de-4505-ad87-5e6dc3dfa4c2', '일의 기쁨과 슬픔', '소설', 11000, NOW()),
     ('d41b537f-3f0b-4d84-9e7f-d33668c4a3bc', '아몬드', '소설', 12000, NOW()),
     ('875125be-c1a4-4496-a7b5-9b2cbf39a2b3', '한국단편소설 40', '소설', 14700, NOW()),
     ('70afb424-7223-42df-9881-96a6284eacc2', '대한민국 요즘 여행', '여행', 16920, NOW());

서버 재구동 후 Postman으로 테스트해보았습니다.

스크린샷 2020-02-17 오후 2 08 09

여기까지 JPA tutorial이었습니다.