본문 바로가기

개발공부/웹개발

스프링 부트와 AWS로 혼자 구현하는 웹 서비스 (~100p)

스프링부트에서 JPA로 데이터베이스를 다뤄보자!

 

ORM이란 Object Relational Mapping (객체지향매핑)

 

MyBatis는 ORM이 아닌 SQL Mapper.

ORM은 객체를 매핑하는 것이고, SQL Mapper는 쿼리를 매핑합니다.

 

예전 :  Spring & MyBatis

최근 동향 : Spring Boot & JPA

 

SQL에 신경써야할 것이 너무 많아짐

 

관계형데이터베이스는 어떻게 데이터를 저장할지에 초점이 맞춰져있음.

객체지향프로그래밍은 기능과 속성을 한 곳에서 관리하는 기술.

 

객체를 데이터베이스에 저장하려하니 문제 발생 => 페러다임 불일치.

 

더이상 SQL에 종속적인 개발을 하지 않기 위해 JPA 사용

JPA는 인터페이스로서 자바 표준명세서.

인터페이스를 사용하기 위해서는 구현체가 필요하며, 대표적으로 Hibernate가 있다.

Spring에서 사용할때는 Spring Data JPA라는 모듈을 사용.

 

Spring Data JPA가 등장한 이유는?

1. 구현체 교체의 용이성 : Hibernate외에 다른 구현체로 쉽게 교체 하기 위함

2. 저장소 교체의 용이성 : 관계형 데이터베이스 외에 다른 저장소로 쉽게 교체하기 위함


이제 실습 시작!

 

  build.gradle에 사용할 jpa랑 h2 의존성들을 등록.

 1. spring-boot-starter-data-jpa

- 스프링 부트용 Spring Data Jpa 추상화 라이브러리

- 스프링 부트 버전에 맞춰 자동으로 JPA관련 라이브러리들의 버전을 관리해줌

 

2. h2

- 인메모리 관계형 데이터베이스

- 별도의 설치 필요없음, 프로젝트 의존성 만으로 관리할 수 있음

- 메모리에서 실행되기 때문에 애플리케이션을 재시작할 때 마다 초기화됨 => 테스트용도로 많이 쓰임

 

domain 패키지 만들기 

: 도메인이란? 게시글, 댓글, 회원, 정산, 결제 등 소프트웨어에 대한 요구사항 혹은 문제영역

 

필자는 어노테이션 순서를 주요 어노테이션을 클래스에 가깝게 둠

 

@Entity : JPA 어노테이션

@Getter, @NoArgsConstructor는 롬복 어노테이션

 

롬복은 코드를 단순화 시켜주지만, 필수 어노테이션은 아님.


✔ Posts 클래스 작성

Posts 클래스는 실제 DB의 테이블과 매칭될 클래스. Entity 클래스라고도 함

JPA를 사용하면 DB데이터에 작업할 경우 실제 쿼리를 날리기 보다, Entity클래스의 수정을 통해 작업함.

 

JPA에서 제공하는 어노테이션들

1.@Entity 

: 테이블과 링크될 클래스

: 기본값으로 클래스의 카멜케이스 이름을 _ 으로 테이블이름에 매칭함

ex) SalesManager.java => sales_manager table

 

2.@Id

: 해당 테이블의 PK필드를 나타냄

 

3.@GeneratedValue

: PK의 생성규칙을 나타냄

: 스프링부트 2.0에서는 GenerationType.IDENTITY 옵션을 추가해야함 auto_increment가 됨

 

4.@Column

: 테이블의 칼럼을 나타내며, 굳이 선언 안해도 클래스의 필드는 모두 칼럼이 됨

: 사용하는 이유는 기본값 외에 추가로 변경이 필요한 옵션이 있으면 사용

ex) 문자열의 경우 VARCHAR가 기본값인데 사이즈를 500으로 늘리고 싶거나, 타입을 텍스트로 변경하고 싶을경우 사용

 

롬복에서 제공하는 어노테이션들

1. @NoArgsConstructor

: 기본 생성자 자동 추가

: public Posts(){} 와 같은 효과

 

2. @Getter 

: 클래스 내 모든 필드의 Getter 메소드를 자동생성

 

3. @Builder

: 해당 클래스의 빌더 패턴 클래스를 생성

: 생성사 상단에 선언 시 생성자에 포함된 필드만 빌더에 포함

 

=> 서비스 초기 구축단계에선 테이블 설계(entity설계)가 빈번하게 변경되는데 

롬복의 어노테이션들은 코드 변경량을 최소화 시켜주기 때문에 적극 사용

 

 

=> Posts 클래스에는 Setter 메소드가 없음.

보통 getter/setter를 무작정 생성하는 경우가 많은데 언제 어디서 변해야하는지 코드상으로 명확하게 구분하기 어려워

차후 기능변경시 정말 복잡해짐(???)

그래서 Entity 클래스에서 절대 Setter 메소드를 만들지 않는다.

해당 필드값의 변경이 필요하면 명확히 그 목적과 의도를 나타낼 수 있는 메소드를 추가해야만 함

ex) 주문 취소의 메소드의 경우. status를 받아서 set 하지 않고, status를 false로 주는 메소드를 호출

 

Setter가 없는 경우에는 생성자를 통해 해당 이벤트에 맞는 public 메소드를 호출

여기서는 @Builder를 통해 제공되는 빌더 클래스를 사용.


✔ JpaRepository를 생성(Posts 클래스로 Database를 접근)

보통 DAO로 불리는 DB Layer 접근다.

JPA에서는 Repository라고 부르며 인터페이스로 생성.

 

JpaRepository<Entity클래스, PK타입>을 상속하면 기본적인 CRUD 메소드가 자동으로 생성됨.

* Entity클래스와 기본 Entity Repository는 함께 위치해야함(도메인 패키지에서 함께 관리)


✔ Spring Data JPA 테스트 코드 작성(PostsRepositoryTest.java)

@After

- Junit에서 단위테스트가 끝날 때 마다 수행되는 메소드를 지정

- 보통은 배포 전 전체 테스트가 수행할 때 테스트간 데이터 침범을 막기 위해 사용

- 여러테스트가 동시에 수행되면 테스트용 DB인 H2에 데이터가 그대로 남아있어 다음 테스트 실행 시 테스트가 실패 할 수 있음

 

@postsRepository.save

- 테이블 posts에 insert/update 쿼리를 실행

- id값이 있다면 update를, 없다면 insert 쿼리가 실행

 

@postsRepository.findAll

- 테이블 posts에 있는 모든 데이터를 조회해오는 메소드

 

별다른 설정없이 @SpringBootTest를 사용할 경우 H2데이터베이스를 자동으로 실행해줌

실행성공!


✔ application.properties 수정 (실행된 쿼리 로그로 확인하기, H2버전이 아닌Mysql 버전을 쿼리에 적용하기)

설정값 추가 완료!

 

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

안되서보니까 deprecated됐다...ㅎ 책산지는 1년 넘은것같은데... 세상이 이렇게 빠르게 변하고있다!!!!!

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect
spring.jpa.properties.hibernate.dialect.storage_engine=innodb
spring.datasource.hikari.jdbc-url=jdbc:h2:mem://localhost/~/testdb;MODE=MYSQL

구글링해서 이 코드로 바꿨음