현대 웹 애플리케이션에서 관계형 데이터베이스(RDB)는 빠질 수 없는 요소이다. Oracle, MySQL 등 다양하게 쓰이고 있고, 그러다보니 객체를 관계형 데이터베이스에서 관리하는 것이 중요하다.
관계형 데이터베이스를 사용하는 상황에서 SQL는 피할 수 없는 요소이다. 하지만 , SQL은 유지보수하는 것이 어렵고, 패러다임 불일치 문제가 발생한다.
관계형 데이터베이스는 어떻게 데이터를 저장할지에 초점이 맞춰져있지만, 객체 지향 프로그래밍 언어는 메시지를 기반으로 기능과 속성을 한 곳에서 관리하는 기술이다.
이 두개의 패러다임이 서로 다른데, 객체를 데이터베이스에 저장하려고 하니 문제가 발생하고, 이를 패러다임 불일치라고 한다.
JPA는 이러한 문제점을 해결하기 위해 등장한다. 서로 지향하는 바가 다른 객체지향 프로그래밍 언어와 관계형 데이터베이스를 중간에서 패러다임을 일치 시켜주기 위한 기술이다.
개발자가 객체지향적으로 프로그래밍을 하고, JPA가 이를 관계형 데이터베이스에 맞게 SQL을 대신 생성해서 실행한다. 이렇게 되면 개발자는 더는 SQL에 종속적인 개발을 하지 않아도 된다.
Spring Data JPA
인터페이스인 JPA를 사용하기 위해서 구현체가 필요하다. 대표적으로 Hibernate, Eclipse 등이 있다. 스프링에서는 이런 구현체들을 직접 다루는 것이 아닌 좀 더 손쉽게 사용하고자 추상화 시킨 Spring Data JPA라는 모듈을 사용해서 JPA 기술을 다룬다.
JPA ← Hibernate ← Spring Data JPA
위와 같은 관계를 같는다.
Hibernate를 사용하는 것과 Spring Data JPA를 사용하는 것에는 차이가 없지만 스프링에서는 Spring Data JPA를 개발했고, 이를 사용하는 것을 권장하고 있다.
Spring Data JPA가 등장한 이유에는 크게 두 가지가 있다.
1. 구현체 교체의 용이성
Hibernate 외에 다른 구현체로 쉽게 교체하기 위함. 만약 Hibernate를 대체하는 새로운 구현체가 생긴다면 쉽게 교체가 가능하다.
2. 저장소 교체의 용이성
관계형 데이터베이스 외에 다른 저장소로 쉽게 교체하기 위함. 관계형 데이터베이스가 한계점이 발생하여 다른 DB로 교체가 필요하다면 의존성만 교체하면 된다.
위와 같은 이점을 갖는 이유는 Spring Data의 하위 프로젝트는 기본적인 CRUD 인터페이스가 같기 때문이다. 즉, Spring Data JPA, Spring Data Redis, Spring Data MongoDB 등 Spring Data의 하위 프로젝트는 save(), findAll 등을 인터페이스로 갖고 있다. 그러다보니 기본적인 기능은 변경할 것이 없다.
JPA에 대해서는 처음 배우는 것이기 때문에 좀 더 공부할 예정!
앞으로 만들 서비스(게시판)의 요구사항
프로젝트에 Spring Data JPA 적용하기
build.gradle에 의존성 추가
implementation('org.springframework.boot:spring-boot-starter-data-jpa') // 스프링 부트용 Spring Data JPA 추상화 라이브러리
implementation('com.h2database:h2') //인메모리 관계형 데이터베이스. 메모리에서 실행되기 때문에 어플리케이션을 재시작할때마다 초기화(테스트용으로 많이 쓰임)
domain이라는 패키지를 생성후 그 안에 posts 패키지와 클래스를 생성 해준다.
package com.jojoldu.book.springboot.domain.posts;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Getter
@NoArgsConstructor //기본 생성자 자동 추가
@Entity //테이블과 링크될 클래스
public class Posts {
@Id //테이블의 PK
@GeneratedValue(strategy = GenerationType.IDENTITY) // PK 생성 규칙 해당 설정값을 추가해야 auto_increment가 된다.
private Long id;
//기본으로 필드는 칼럼이지만 옵션을 변경하고 싶을 때 사용한다. 문자열의 경우 VARCHAR(255)가 기본값이지만 사이즈를 500으로 늘릴 때
@Column(length = 500, nullable = false)
private String title;
//타입을 TEXT으로 변경하고 싶을 때
@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;
}
}
setter를 무작정 생성하게 되면 인스턴스 값이 언제 어디서 변하는지 명확하게 구분이 어렵기 때문에 Entity 클래스에서는 Setter메소드를 만들지 않는다. 대신 값 변경이 필요하면 목적과 의도를 나타낼 수 있는 메소드를 추가한다.
그러면 어떻게 값을 DB에 채워넣을까?
기본적으로는 생성자를 통해 최종값을 채운 후 DB에 삽입하며, 값 변경이 필요할 때 해당 이벤트에 맞는 public 메소드를 호출하여 변경하는 것을 전제로 한다. 이 책에서는 @Builder를 통해서 사용한다. 생성자나 빌더나 생성 시점에 값을 채워주는 것은 동일하지만 생성자는 지금 채워야할 필드가 무엇인지 명확히 지정할 수 없다.
빌더를 사용하면 어느 필드에 어떤 값을 채워야할지 명확하게 인지할 수 있다.
이후, Posts클래스로 Database를 접근하게 해줄 JpaRepository를 생성해준다.
package com.jojoldu.book.springboot.domain.posts;
import org.springframework.data.jpa.repository.JpaRepository;
public interface PostRepository extends JpaRepository<Posts, Long> {
}
해당 인터페이스는 보통 Dao라고 불리는 DB Layer 접근자이다. JPA에서는 Repository라고 부르며 인터페이스로 생성해준다. 인터페이스 생성후 JpaRepository<Entity클래스, PK 타입>을 상속하면 기본적인 CRUD메소드가 자동으로 생성된다. 또한 @Repository 추가할 필요도 없다.
주의해야하는 점은 Entit클래스와 기본 Entity Repository는 함께 위치해야한다.
테스트 코드 작성하기
package com.jojoldu.book.springboot.domain.posts;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest //설정없이 사용하면 자동으로 H2 DB를 사용
public class PostsRepositoryTest {
@Autowired
PostsRepository postsRepository;
@After //junit에서 단위 테스트가 끝날때마다 실행하는 메소드
public void cleanup(){
postsRepository.deleteAll();
}
@Test
public void 게시글저장_불러오기(){
//given
String title = "테스트 게시글";
String content = "테스트 본문";
//save : 테이블 posts에 insert/update 쿼리 실행. id값이 있다면 update, 없다면 insert
postsRepository.save(Posts.builder()
.title(title)
.content(content)
.author("yoon828990@gmail.com")
.build());
//when
List<Posts> postsList = postsRepository.findAll(); //findAll : posts에 있는 모든 데이터 조회
//then
Posts posts = postsList.get(0);
assertThat(posts.getTitle()).isEqualTo(title);
assertThat(posts.getContent()).isEqualTo(content);
}
}
만약 실행되는 쿼리문을 보고싶다면 다음과 같은 파일을 생성해준다.
src/main/resources/application.properties
그리고 그 안에 다음과 같이 작성해준다.
spring.jpa.show-sql=true
그리고 다시 테스트를 돌려보면 콘솔에서 쿼리로그를 확인할 수 있다.
쿼리문을 MySQL 버전으로 변경하고 싶다면 application.propertis 파일안에 아래와 같이 추가해준다.
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
MySql 문법으로 변경된 것을 확인할 수 있다.
참고
http://www.yes24.com/Product/Goods/83849117
'개발 공부 > Spring' 카테고리의 다른 글
[Spring+MySql] Spring에 AWS RDS(MySQL) 연결하기 / driver 에러 해결 (0) | 2023.03.06 |
---|---|
[SpringBoot] 등록/수정/조회 API 만들기 - 그리고 중단.. (0) | 2023.01.31 |
[SpringBoot] 롬복 소개 및 설치 (0) | 2023.01.27 |
[Springboot] 스프링 부트에서의 테스트 코드 (0) | 2023.01.26 |
[SpringBoot] 스프링부트 프로젝트 세팅하기 (0) | 2023.01.25 |