이 포스팅에서는 스프링부트와 JPA를 연동하는 실습 과정을 담았습니다.

JPA?

  • JPAJava Persistent API 의 약자입니다.
  • 영속성(persistence) 관리ORM 을 위한 표준 기술입니다.
  • 그러면 영속성이 뭔지.. 그리고 ORM이 뭔지 먼저 알아 보겠습니다.
  • 영속성 은 Entity에 데이터를 영구히 저장하기 위한 환경 정도라고 알아두시면 될 것 같습니다. 영속성과 관련해서 자세히 알고 싶은 분은 아래 블로그들을 방문하여 참고하시면 좋을거 같습니다.
  • ORM 은 Object-relational mapping의 약자로, 객체와 관계의 설정 즉, DB 데이터를 하나의 객체로 매핑해주는 것을 뜻합니다.
  • JPA는 인터페이스로 이를 구현하는 구현체로는 Hibernate, OpenJPA, EclipseLink 등등...이 있다고 하네요. 스프링부트는 기본적으로 하이버네이트를 사용합니다.

스프링부트에 JPA 끼얹기

프로젝트 구조

스크린샷 2019-08-26 오후 2.40.41.png

1. pom.xml에 의존성 추가

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
</dependency>
<!--테스트용 인메모리 DB-->
<dependency>
      <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
</dependency>

H2 참고

h2인메모리 기반으로 경량 DB로 테스트용, 캐시용으로 자주 사용됩니다.

h2는 데이터베이스에 접근할 수 있는 웹 인터페이스를 제공합니다.
h2를 사용하기 위해서는 spring-boot-devtools의존성 추가가 필요합니다.

어플리케이션을 실행하고 http://localhost:8080/h2-console 에 접속하면 콘솔을 확인할 수 있습니다.
참고로 스프링부트 최신버전을 사용하고 있다면, JDBC URL을 jdbc:h2:mem:testdb로 바꾸시고 연결하시길 바랍니다.

콘솔 주소는 application.properties파일에서 변경할 수 있습니다:

spring.h2.console.path=/db-console

DB 내용은 어플리케이션이 시작할때마다 초기화됩니다.

자세한 내용은 여기서 확인 하시면 되겠습니다.

2. Entity class 작성

참고로 Lombok을 사용했습니다.

매번 DB에 insert, update 할 때마다, 날짜 데이터를 등록 수정하기 힘듭니다. 그래서 저희는 JPA Auditing을 사용합니다.

사용 방법은

  1. Entity 클래스들의 상위 클래스 작성(우리 프로젝트에선 BaseTimeEntity)
  2. Entity 클래스에서 상속
  3. @EnableJpaAuditing 활성화
    이 순으로 됩니다.

그럼 먼저, 상위 클래스인 BaseTimeEntity를 작성해봅시다.

package com.corini.springbootjpa;

import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import java.time.LocalDateTime;

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract  class BaseTimeEntity {
    @CreatedDate
    private LocalDateTime createdDate;


    @LastModifiedDate
    private LocalDateTime modifiedDate;
}

그 다음엔 Entity 클래스에서 상속을 받습니다.

package com.corini.springbootjpa.account;

import com.corini.springbootjpa.BaseTimeEntity;
import lombok.*;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Getter
@Setter
@ToString
public class Account extends BaseTimeEntity{

    @Id @GeneratedValue
    private Long id;

    private String username;
    private String password;

    @Builder
    public Account(String username, String password) {
        this.username = username;
        this.password = password;
    }

}

마지막으로 SpringBootApplication 클래스에서 Auditing을 활성화 합니다.

package com.corini.springbootjpa;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@EnableJpaAuditing
@SpringBootApplication
public class SpringbootJpaApplication {

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

}

Entity 클래스 관련 어노테이션 정리

  • @Entity
    • 테이블과 매핑될 클래스임을 나타냅니다.
    • 스네이크 표기법 으로 이름을 매칭합니다.
    • ex) UserInfo.java - > user_info 테이블
  • @Id
    • 테이블의 PK 컬럼을 뜻합니다.
  • @GeneratedValue
    • PK 생성 규칙을 뜻 합니다.
    • 기본값은 AUTO, int type의 auto increment 같이 자동증가 옵션입니다.
  • @Column
    • 굳이 선언을 하지 않아도, 클래스의 필드는 모두 컬럼이 됩니다.
    • 사용이유는 기본값이외에 설정을 하고 싶을 때 사용합니다.
    • 예를 들어 VARCHAR(255) 기본 값을 500으로 늘린다던가...
  • @Builder
    • 해당 클래스의 빌더패턴 클래스를 생성
    • 생성자 상단에 선언 시, 생성자에 포함된 필드만 빌더에 포함
  • @NoArgsConstructor
    • 기본 생성자 자동 추가
    • access = AccessLevel.PROTECTED : 생성자 접근 권한을 protected로 제한
  • @Table
    • 테이블명과 클래스명이 다른 경우 사용합니다.
    • @Table(name="tbl_name")
    • 위와 같이 어노테이션을 지정하면, 클래스 명이 아닌 지정한 명칭대로 테이블을 생성합니다.

3. AccountRepository 작성

package com.corini.springbootjpa.account;

import org.springframework.data.jpa.repository.JpaRepository;

public interface AccountRepository extends JpaRepository<Account, Long> {
}

JpaRepository<Entity클래스, PK타입>를 상속하면 기본적인 CRUD 메소드가 자동생성 됩니다.
@Repository 어노테이션을 추가할 필요가 없습니다.
mac + intellij 사용 중이시면 AccountRepository 인터페이스 명에 커서를 갖다놓고 command + shift + T 단축키를 누르면 테스트 코드를 위한 클래스를 자동으로 생성할 수 있습니다.

4. AccountController 작성

package com.corini.springbootjpa.account;

import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@AllArgsConstructor
public class AccountController {

    private AccountRepository accountRepository;

    @GetMapping(value = "/")
    public String hello() {
        return "hello world";
    }

    @GetMapping(value = "/create")
    public Object create() {
        Account account = new Account();
        account.setUsername("admin");
        account.setPassword("test");

        accountRepository.save(Account.builder()
        .username("admin")
        .password("test")
        .build());

        return accountRepository.findAll();
    }

    @GetMapping(value = "/read")
    public Object read() {
        return accountRepository.findAll();
    }

    @GetMapping(value = "/delete")
    public Object delete() {
        accountRepository.deleteAll();
        return "delete";
    }
}

@AllArgsConstructor 어노테이션을 사용하여, 생성자를 생성합니다. 코드로 보시면 이해 되실 겁니다.

@RestController
public class AccountController {
  private AccountRepository accountRepository;

  public AccountController(AccountRepository accountRepository) {
    this.accountRepository = accountRepository;
  }
  ...
}

이렇게 하는 이유는 스프링프레임워크에서 Bean 주입 방식 중 권장하는 방법입니다. 다른 방법으로는...

  • @Autowired
  • setter
    방식이 있습니다.

http://localhost:8080/create url을 호출하면 Account 테이블의 username 컬럼에는 admin 데이터가 password 컬럼에는 test 데이터가 들어갑니다.
위에 정의한대로 /read url은 전체 데이터 조회를 /delete url은 전체 데이터 삭제를 합니다.

테스트 코드를 짜시고 본 프로젝트에 적용하는 걸 추천합니다... 저는 간단한 코드라 그냥 짰지만... ㅠㅠ

jojoldu님 블로그@junwoo4690님 벨로그 포스팅 참조하시면 테스트 코드 작성 방법 자세히 나와 있습니다!

정리

간단하게나마 인메모리 기반 데이터베이스를 h2를 이용하여, JPA를 사용해봤습니다!

맛보기로 해봤지만, 넘나 편리한거 같습니다...

퇴사했던 회사에서는 springMVC + JDBC + Mybatis + MSSQL 사용했었는데... 비즈니스 로직 짜는거 보다 설정이나 이외의 것들 코딩하느라 시간이 엄청 오래 걸렸었는데... spring boot + JPA 조합은 정말 신세계네요..

저의 소스는 Github에서 확인 하실 수 있습니다!!!

Reference