이전 문서 Spring boot REST API 서버만들기 - 1 를 참고하자. 이전 문서에서는 딱 REST API를 호출하는 정도까지만 했다. 이제 데이터베이스 연결을 해볼 것이다. 이 문서는 강의가 아닌 학습 과정을 정리한 문서다.
JPA
JPA(Java Persistence API)는 Java 객체가 생성한 응용 프로그램의 프로세스보다 오래 지속되는 정보에 대한 관리 메커니즘을 의미한다. 데이터베이스가 가장 대표적인 경우가 될 것이다. JPA 그 자체는 도구나 프레임워크가 아니다. 도구 또는 프레임워크로 구현 할 수 있는 일련의 개념을 정의 한다.
예를 들어 JPA의 ORM(Object-relational mapping) 모델은 원래는 Hibernate를 기반으로 했지만 이후로도 계속 발전하고 있다. 마찬가지로 JPA는 원래 관계형(relational)/SQL 데이터베이스와 함께 사용하도록 고안되었지만 일부 JPA 구현은 NoSQL 데이터 저장소와 함께 사용하도록 확장되었다. NoSQL을 지원하는 JPA의 일반적인 프레임워크는 JPA 2.2의 참조 구현인 EclipseLink다.
JPA와 Hibernate
Gavin King이 개발하고 2002년 출시한 Hibernate는 Java용 ORM 라이브러리다. King은 entity beans의 대안으로 Hibernate를 개발했다. 이 프레임워크는 매우 인기가 있었고 또 필요했기 때문에 많은 아이디어가 JPA 사양에 채택되고 체계화되었다. 오늘날 Hibernate ORM은 가장 성숙한 JPA 구현 중 하나다. Hibernate ORM 5.3.8은 JPA 2.2를 구현하고 있다.
Java ORM
JPA 구현은 일종의 ORM 계층을 제공한다. 따라서 JPA와 JPA 호환 툴 들을 이해하려면 ORM에 대해서 잘 알고 있어야 한다.
Object-relation mapping은 개발자가 수동으로 해야 할 작업을 자동화 시킨다는 점에서 충분한 이유가 있는 작업이다. Hibernate ORM 또는 EclipseLink와 같은 프레임워크는 데이터베이스 작업을 프레임워크인 ORM 계층으로 코드화 시킨다. 이는 응용 프로그램 아키텍처의 일부로 ORM 계층을 둠으로써 관계형 데이터베이스의 테이블 및 컬럼을 소프트웨어 객체로 변환할 수 있다. 즉 Java 클래스 및 객체를 관계형 데이터베이스에 저장하고 관리 할 수 있게 된다.
기본적으로 persisted 객체 이름은 테이블 이름이 되고 필드는 컬럼이 된다.
최근들어 비 관계형 데이터베이스(non-relation databases)에 대한 관심이 높아지면서 Java 개발자들은(다른 언어 개발자도 마찬가지) 다양한 NoSQL 데이터베이스를 사용하고 있다. Hibernate OGM과 EclipseLink를 포함한 몇몇 JPA 구현은 NoSQL을 수용하도록 발전하고 있다.
JPA를 사용해서 새 포로젝트를 설장할 때는 데이터 저장소(datastore)와 JPA 제공자를 설정해야 한다. 선택한 데이터베이스(SQL 혹은 NoSQL)에 연결하도록 데이터 저장소 컨텍터를 구성한다. 또한 Hibernate나 EclipseLink와 같은 프레임워크를 사용하도록 구성 할 수 있다. JPA를 수동으로 구성할 수 있지만 많은 개발자들이 Spring에 맡기고 있다.
JPA 설정
다른 모든 현대적인 프레임워크와 마찬가지로 JPA도 컨벤션으로 코딩하는 방식을 적용하고 있다. 일례로 Musician이라는 클래스는 기본적으로 Musician이라는 데이터베이스 테이블에 매핑된다.
기존 구성에도 JPA를 사용 할 수 있는데, 얘를 들어 JPA의 @Table 어노테이션을 사용해서 Musician 클래스가 저장될 테이블을 지정 할 수 있다.
@Entity
@Table(name="musician")
public class Musician {
// ..class body
}
Primary Key
JPA에서 primary key는 데이터베이스의 각 오브젝트를 유일하게 식별하기 위해서 사용하는 필드다. Primary key는 객체를 참조하고 다른 엔티티와 연결하는데 유용하다.
@Entity
public class Musician {
@Id
private Long id;
}
이 경우 JPA의 @Id 어노테이션을 사용하여 id 필드를 Musician의 프라이머리 키로 설정했다. 이 필드는 기본적으로 auto-increment로 설정된다.
CRUD 연산
클래스를 데이터베이스 테이블에 맵핑하고 프라이머리 키를 설정하면 데이터베이스에 대한 create, retrieve, delete, update 가 가능해진다.
Review API 개발
Review API를 개발한다. 테이블은 간단하게 만들었다.
JPA를 위한 의존성 추가
build.gradle에 "org.springframework.boot:spring-boot-stater-data-jpa" 와 "com.h2database:h2" 의존성을 등록한다.
ibatis나 MyBatis에서 DAO로 부르는 데이터베이스 레이어 접근자다. JPA에서는 Repository라고 부르는 인터페이스로 만든다. JpaRepository<Entity Class, PK 타입>을 상속하면 기본적인 CRUD 메서드가 자동으로 생성된다. Entity 클래스와 Entity Repository는 함께 위치해야 한다.
등록 API 만들기
API를 만들기 위해서는 3개의 클래스가 필요하다.
Request 데이터를 받을 DTO
API를 받을 Controller
트랜잭선, 도메인 기능간의 순서를 보장하는 Service
Spring web의 계층은 아래와 같다.
Web Layer : 컨트롤러(@Controller), freemarker등 뷰, 템플릿을 포함하는 영역이다. 이외에도 필터(@Filter), 인터셉터, 컨트롤러 어드바이스(@ControllerAdvice)등 외부 요청과 응답을 다룬다.
Service Layer : @Service를 다루는 영역이다. Controller와 DAO의 중간 영역에 위치한다. @Transactional도 포함한다.
Repository Layer : 데이터베이스 저장소에 접근하는 영역.
DTOs : 계층간 데이터 교환을 하는 객체
Domain Model : 특정 범위의 개발 대상을 모든 사람들이 동일한 관점에서 이해, 공유 할 수 있도록 단순화시킨 모델이다. 쇼핑몰 서비스라면 장바구니, 구매, 지불 시스템 등이 도메인이 될 수 있다. @Entity가 사용된 영역이 도메인 모델이다.
DTO를 만들어보자. 리뷰를 저장하는 DTO를 만들었다.
package co.kr.joinc.Todo.dto.review;
import co.kr.joinc.Todo.domain.review.ReviewEntity;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor
public class ReviewPostRequestDto {
private String content;
private Long user_id;
@Builder
public ReviewPostRequestDto(String content, Long user_id) {
this.content = content;
this.user_id = user_id;
}
public ReviewEntity toEntity() {
return ReviewEntity.builder()
.content(content)
.user_id(user_id)
.build();
}
}
Review Controller과 Service 등록
Review 서비스 클래스인 ReviewPostService 클래스를 만들었다.
package co.kr.joinc.Todo.service.review;
import co.kr.joinc.Todo.domain.review.ReviewRepository;
import co.kr.joinc.Todo.dto.review.ReviewPostRequestDto;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
@RequiredArgsConstructor
@Service
public class ReviewPostService {
private final ReviewRepository reviewRepository;
@Transactional
public Long save(ReviewPostRequestDto requestDto) {
return reviewRepository.save(requestDto.toEntity()).getId();
}
}
@Transactional 어노테이션은 transaction begin, commit를 자동으로 수행한다.
예외가 발생할 경우 rollback 처리를 자동으로 수행한다.
review controller 코드다.
package co.kr.joinc.Todo.controller.review;
import co.kr.joinc.Todo.dto.review.ReviewPostRequestDto;
import co.kr.joinc.Todo.service.review.ReviewPostService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RequiredArgsConstructor
@RestController
public class ReviewPostController {
private final ReviewPostService reviewPostService;
@PostMapping("/api/v1/review")
public Long save(@RequestBody ReviewPostRequestDto requestDto) {
return reviewPostService.save(requestDto);
}
}
테스트
애플리케이션을 실행하고 H2 Console에 접근했다.
로그인 화면이 뜬다. application.properties에 설정한 datasource.{url,username,password}로 로그인 한다.
review_entity 테이블과 각 필드들이 ReviewEntity Class와 맵핑되서 만들어진 것을 확인 할 수 있다.
curl 테스트용 데이터를 만들었다.
Contents
JPA
JPA와 Hibernate
Java ORM
JPA 설정
Primary Key
CRUD 연산
Review API 개발
JPA를 위한 의존성 추가
H2 데이터베이스
Entity 클래스 생성
등록 API 만들기
Review Controller과 Service 등록
테스트
정리
참고
Recent Posts
Archive Posts
Tags