SpringBoot에서 JPA 사용하기

신광진·2021년 6월 5일
0
post-thumbnail

OVERVIEW

이번 포스팅에서는 SpringBoot에서 실제로 JPA를 사용하기 위한 설정, 코드작성과 간단한 테스트를 실행해본다.

SpringBoot에 JPA 적용하기

1. 의존성 추가

우선 build.gradle 하단에 dependencies에 위에 2줄을 추가한다.

  • spring-boot-starter-data-jpa
    • 스프링 부트용 Spring Data JPA 추상화 라이브러리
    • 스프링 부트 버전에 맞춰 자동으로 JPA관련 라이브러리 버전을 관리해준다.
  • h2
    • 별도의 설치없이 프로젝트 의존성만으로 관리 가능한 인메모리 관계형 데이터베이스
    • 인메모리이기 때문에 애플리케이션을 재시작 할 때 초기화되며 테스트용으로 많이 사용한다.

2. 패키지 생성

의존성을 추가하였다면 Posts라는 도메인 클래스를 생성한다.

  • domain
    • domain이란 소프트웨어의 요구사항 혹은 문제영역이라고 할 수 있다.
    • 기존에 SQLMapper를 사용할 때 xml파일에 SQL을 작성하고 클래스에 쿼리의 결과를 담는 과정이 domain클래스에서 모두 해결된다.

3. Entity클래스 작성

Posts 클래스의 내용은 위와 같다.
내용을 하나하나 살펴보자.

  • @Getter
    • 필드변수의 Getter를 생성해주는 롬복 어노테이션이다.
  • @NoArgsConstructor
    • 매개변수 없는 생성자를 만들어주는 롬복 어노테이션이다.
    • 여기서는 public Posts() {} 와 같다.
  • @Entity
    • 해당 클래스가 DB테이블과 매칭될 클래스라는 의미이다.
    • 해당 어노테이션이 있는 클래스를 Entity클래스라고도 부른다.
    • JPA를 사용하면 DB데이터에 작업을 할 때 Entity클래스를 수정하여 작업한다.
    • 클래스의 카멜케이스와 테이블의 언더바를 매핑한다.
      ex) SalesManager.java -> sales_manager table
  • @Id
    • 해당 테이블의 PK 필드를 나타낸다.
  • @GeneratedValue
    • PK 생성 규칙을 의미한다.
    • 스프링 부트 2.0 에서는 GenerationType.IDENTITY 옵션을 추가해야만 auto_increment가 된다.
  • @Column
    • 테이블의 컬럼을 나타낸다.
    • Entity클래스의 필드변수들은 기본적으로 Column이 되지만 기본값 외에 추가로 변경이 필요한 옵션이 있는경우에 사용한다.
      ex) 문자열의 경우 VARCHAR(255)가 기본값이지만, 사이즈를 500으로 늘리거나 타입을 TEXT로 변경하는 등의 작업을 할 때 사용한다.
  • @Builder
    • 해당 클래스의 빌더 패턴 클래스를 생성하는 롬복 어노테이션이다.
    • 생성자 상단에 선언하면 샌성자에 포함된 필드만 빌더에 포함된다.

Posts클래스에는 Setter가 없다.
왜 Setter를 만들지 않고 Builder패턴을 사용하는 것일까?

첫 번째 Entity클래스에 Setter가 없는 이유는 해당 클래스의 인스턴스 값들이 언제 어디서 변해야 하는지 코드상으로 명확하게 구분할 수 없기 때문이다.
Setter를 사용하지 않는 대신 해당 필드의 값 변경이 필요한 경우 명확한 목적과 의도를 나타낼 수 있는 메소드를 추가해서 사용해야 한다.
올바른예시와 잘못된 예시를 들어보자.

[잘못된 예시]

public class Order {
  public void setStatus(boolean status) {
      this.status = status;
  }
}

public void 주문서비스_취소이벤트() {
	order.setStatus(false);
}	

[올바른 예시]

public class Order {
  public void cancelOrder() {
      this.status = false;
  }
}

public void 주문서비스_취소이벤트() {
	order.cancelOder();
}

이 부분을 읽으면서 개인적으로 책을 구매하기 잘했다는 생각을 했다.
잘못된 예시는 너무나도 흔히 사용하는 코드이기 때문이다.
사실 두 가지를 비교하기 전에는 잘못된 예시가 뭐가 문제지? 라는 생각이었다.
하지만 목적과 의도를 명확하게 나타내는 메소드를 만들어서 사용하는것이 얼마나 코드를 읽기 편하개 만들어주는지 알 수 있었다.

두 번째 Builder패턴을 사용하는 이유 역시 명확성을 위해서다.
Builder패턴을 사용하면 어떤 필드에 어떤 값을 넣는지 명확하게 알 수 있지만 생성자를 사용할 경우에는 어떤 필드에 어떤 값을 넣는지 명확하게 알 수 없다.
생성자를 이용하는 방법과 Builder패턴을 사용하는 경우를 비교하는 예제는 작성하지 않는다.

4. JpaRepository 작성

JpaRepository는 Entity클래스로 Database를 접근하게 해준다.
MyBatis를 사용할 때 만드는 Dao라고 얘기하는 DB Layer 접근자와 매핑되는 개념이다.

JPA는 Interface로 생성하며 JpaRepository<Entity 클래스, PK 타입>를 상속하면 기본적인 CRUD 메소드가 자동으로 생성된다.
@Repository를 별도로 추가하지 않아도 된다.
여기서 주의점은 Entity클래스와 JpaRepository는 같은 곳에 위치 해야한다는 것이다.
둘은 아주 밀접한 관계이며 Repository없이는 Entity클래스는 제 역할을 할 수 없기 때문이다.
그래서 이 둘은 Domain패키지에서 함께 관리한다.

5. H2 데이터베이스로 테스트 하기

여기까지 JPA를 사용하기 위해 의존성을 추가하고, Entity클래스와 JpaRepository를 생성하였다.
의존성 추가시에 함께 추가한 H2 데이터베이스를 이용하여 잘 동작하는지 테스트 해보자.

savefindAll기능을 테스트 하는 코드다.
Junit 사용방법은 이전 포스팅을 참고하면 된다.
별다른 설정없이 @SpringBootTest를 사용하면 H2 데이터베이스를 자동으로 실행해 준다.
여기서는 처음 사용해보는 어노테이션과 Jpa의 메소드에 대해서만 간략히 설명한다.

  • @After
    • Junit에서 단위 테스트가 끝날 때 마다 실행할 메소드를 지정하는 어노테이션이다.
    • 보통 배포 전 전체 테스트를 수행할 때 테스트 간 데이터 침범을 막기위해 많이 사용한다.
    • 여기서는 단위 테스트가 끝날 때 마다 H2 데이터 베이스를 초기화 하는데 사용하고 있다.
  • postsRepository.save
    • 테이블 Posts에 insert/update 쿼리를 수행한다.
    • id(pk)값이 존재한다면 update, 존재하지 않는다면 insert를 수행한다.
  • postsRepository.findAll
    • SELECT * FROM POSTS와 동일하다.

만약 실제로 수행되는 쿼리가 궁금하다면 application.properties에 간단한 설정을 추가하면 된다.

spring.jpa.show_sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect

첫 번째 설정은 실행된 쿼리문을 콘솔에 보여준다는 설정이다.
두 번째 설정은 쿼리문을 MySQL문법으로 보겠다는 설정이다.
두 번째 설정을 하지 않으면 H2 데이터베이스 문법이 적용되기 때문에 우리가 기대한 쿼리와 다른 결과가 나올 수 있으니 두 가지 설정 모두 적용하자.

SUMMARY

  • Entity클래스는 실제 데이터베이스 테이블과 매핑되는 클래스이다.
  • Entity클래스로 데이터베이스 접근을 가능하게 해주는것은 JpaRepository이다.
  • JpaRepository는 MyBatis의 Dao(DB Layer)와 매핑되는 개념이다.
  • Entity클래스와 JpaRepository는 같은 곳에 위치해야 하며 보통 Domain Package에서 함께 관리한다.
  • JpaRepository는 Interface로 만들며
    JpaRepository<Entity 클래스, PK 타입>를 상속하면 기본적인 CRUD메소드가 자동생성 된다.
  • 테스트용으로 H2 데이터베이스를 활용하며 데이터 침범이 일어나지 않도록 주의해야 한다.
profile
이거 왜안되냐

0개의 댓글