스프링 부트에서 JPA로 데이터베이스 다루기-1
JPA?
현대의 웹 애플리케이션에서 관계형 데이터베이스는 빠질 수 없는 요소기 때문에 객체를 관계형 데이터베이스에서 관리하는 것이 중요하다. 개발자가 아무리 자바 클래스를 잘 설계해도 SQL을 통해야만 데이터베이스에 저장하고 조회할 수 있다. 수십 수백 개의 테이블을 만들고 유지보수해야 하며 객체지향은 기능과 속성을 한 곳에서 관리하는 기술이기 때문에 패러다임 불일치 문제가 생긴다.
JPA는 이러한 문제를 해결하기 위해 서로 지향하는 바가 다른 2개의 영역을 중간에서 패러다임 일치를 시켜주기 위한 기술이다. 개발자는 객체지향적으로 프로그래밍을 하고 JPA가 SQL를 알맞게 대신 생성해서 실행 한다. 즉, SQL에 종속적인 개발을 하지 않아도 된다는 것이다.
SpringData JPA
JPA는 인터페이스로서 자바 표준 명서세이다. Spring에서는 JPA를 사용할 때 직접 다루지 않고 SpringData JPA라는 모듈을 이용하여 JPA기술을 다룬다. 이렇게 사용하는 이유는 구현체 교체의 용이함과 저장소 교체의 용이함이 있다. 새로운 JPA 구현체가 있다면 쉽게 교체가 가능하며 관계형 데이터베이스 외에 다른 저장소로 쉽게 교체할 수 있다. 이러한 것이 가능한 이유는 CRUD의 인터페이스가 같기 때문이다.
프로젝트에 Spring Data JPA 적용하기
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'com.h2database:h2'
spring-boot-starter-data-jpa
- 스프링 부트용 Spring Data Jpa 추상화 라이브러리
- 스프링 부트 버전에 맞춰 자동으로 JPA관련 라이브러리들의 버전을 관리해준다.
h2
- 인메모리 관계형 데이터베이스
- 별도의 설치가 필요 없이 프로젝트 의존성으로만 관리할 수 있다
- 메모리에서 실행되기 때문에 애플리케이션을 재시작할 때 마다 초기화 된다는 점을 이용하여 테스트 용도로 많이 쓰인다.
Posts.class
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@Getter
@NoArgsConstructor
@Entity
public class Posts {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(length = 500, nullable = false)
private String title;
@Column(columnDefinition = "TEXT", nullable = false)
private String content;
private String author;
@Builder
public Posts(String title, String content, String author) {
this.title = title;
this.content = content;
this.author = author;
}
}
@Entity
- 테이블과 링크될 클래스를 나타낸다.
- 기본값으로 클래스의 카멜케이스 이름을 언더스코어 네이밍으로 테이블 이름을 매칭한다.
- ex) SaleManager.java -> sale_manager table
@Id
- 해당 테이블의 PK필드를 나타낸다.
@GeneratedValue
- PK의 생성 규칙을 나타낸다.
- 스프링 부트 2.0 에서는 Generation Type.IDENTITY 옵션을 추가해야만 auto_increment가 된다
@Column
- 테이블의 칼럼을 나타내며 굳이 선언하지 않더라도 해당 클래스의 필드는 모두 칼럼이 된다.
- 사용하는 이유는, 기본값 외에 추가로 변경이 필요한 옵션이 있으면 사용한다.
- 문자열 사이즈를 늘리고 싶거나 타입을 TEXT로 변경하고 싶은 경우 등의 사용된다.
@NoArgsConstructor
- 기본 생성자 자동 추가
- public Posts(){}와 같은 효과
@Getter
- 클래스 내 모든 필드의 Getter 메서드를 자동생성
@Builder
- 해당 클래스의 빌더 패턴 클래스를 생성
- 생성자 상단에 선언 시 생성자에 포함된 필드만 빌더에 포함
어노테이션들은 코드 변경량을 최소화시켜 주기 때문에 적극적으로 사용하는 것이 좋다.
해당 클래스에는 Setter 메서드가 없다. 자바빈 규약을 생각하면서 getter/setter를 무작정 생성하는 경우 해당 클래스의 인스턴스 값들이 언제 어디서 변해야 하는지 코드상으로 명확히 구분할 수 없어 기능 변경 시 복잡해진다. 이러한 이유로 Entity 클래스에서는 절대 Setter 메서드를 만들지 않는다. 필요하다면 명확히 목적과 의도를 나타낼 수 있는 메서드를 추가해야만 한다.
그러면 Setter가 없는 상황에서 어떻게 값을 채워 DB에 삽입해야 할까? 기본적인 구조는 생성자를 통해 삽입하며 값 변경이 필요한 경우 해당 이벤트에 맞는 public 메서드를 호출하여 변경하는 것을 전제로 한다. 해당 코드는 @Builder를 통해 제공되는 빌더 클래스를 사용한다. 생성자나 빌더나 생성 시점에 값을 채워주는 역할을 동일하다. 하지만 생성자의 경우 지금 채워야 할 필드가 무엇인지 명확히 지정할 수 없다.
예를 들면 다음과 같다.
public Example(String a, String b){
this.a = a;
this.b = b;
}
new Example(b,a)처럼 a,b 위치를 변경해도 코드를 실행하기 전까지 문제를 찾을 수 없다.
Example.builder()
.a(a)
.b(b)
.build();
빌더를 사용하게 되면 다음과 같이 어느 필드에 어떤 값을 채워야 할지 명확하게 인지 할 수 있다.