🚧

Spring boot 에 Spring Data JPA μ μš©ν•˜κΈ°

purpplee 2021. 12. 29. 16:25

ν”„λ‘œμ νŠΈμ— Spring Data JPA μ μš©ν•˜κΈ°

build.gradle μˆ˜μ •

Spring Data JPA 와 H2 λ₯Ό μ‚¬μš©ν•˜κΈ° μœ„ν•΄ μ•„λž˜μ²˜λŸΌ build.gradle νŒŒμΌμ„ μˆ˜μ •ν•œλ‹€. Spring Data JPA κ°€ 무엇이고 SQL Mapperμ™€λŠ” μ–΄λ–»κ²Œ λ‹€λ₯Έμ§€ κΆκΈˆν•˜λ‹€λ©΄... πŸ‘‰ SQL Mapper vs ORM (feat. Hibernate vs Spring Data JPA) H2λŠ” 인메λͺ¨λ¦¬ RDBMS둜 Spring boot 에 λ‚΄μž₯λ˜μ–΄ μžˆλ‹€. λ³„λ„μ˜ μ„€μΉ˜κ°€ ν•„μš” μ—†κ³  λ©”λͺ¨λ¦¬μ—μ„œ μ‹€ν–‰λ˜λ―€λ‘œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ‹€ν–‰ μ‹œ μ΄ˆκΈ°ν™”λΌμ„œ ν…ŒμŠ€νŠΈ μš©λ„λ‘œ 자주 μ“°μ΄λŠ” DB 이닀. μˆ˜μ •ν•΄μ£Όκ³  gradle 반영 λ²„νŠΌμ„ κΌ­ λˆ„λ₯΄μž.

dependencies {
	...

    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    runtimeOnly 'com.h2database:h2'
}

 

domain 생성

domain 은 μš”κ΅¬ 사항 및 문제 μ˜μ—­μ΄λ‹€. λΈ”λ‘œκ·Έλ‘œ 따지면 κ²Œμ‹œκΈ€, νšŒμ›, λ°©λͺ…둝, λŒ“κΈ€ 등등에 ν•΄λ‹Ήλœλ‹€. 그리고 λ„λ©”μΈμ—μ„œ λ„μΆœλœ 객체듀을 domain object 라고 ν•œλ‹€. 데이터 객체인 Entity, λ°μ΄ν„°μ˜ μ˜μ†μ„±μ„ μœ„ν•œ Repository λ“± λͺ¨λ‘ domain object 에 ν¬ν•¨λœλ‹€. 데이터 전달을 μœ„ν•΄ μ‚¬μš©λ˜λŠ” DTO μ™€λŠ” λͺ©μ μ΄ λ‹€λ₯΄κΈ° λ•Œλ¬Έμ—, DTO 에 쿼리 κ²°κ³Όκ°’λ§Œ λ‹΄μ•˜λ‹€λ©΄ domain object μ—λŠ” 문제 해결을 μœ„ν•œ λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§λ„ ν¬ν•¨λœλ‹€.(μ§€κΈˆμ€ μ΄λ ‡κ²Œλ§Œ μ΄ν•΄ν•˜κ³ ... λ‚˜μ€‘μ— 도메인 νŒ¨ν„΄μ„ μ œλŒ€λ‘œ 곡뢀해봐야겠닀)

 

src/main/java/com.study.springbootsaws/domain/posts/Posts

Posts ν΄λž˜μŠ€λŠ” Spring Data JPA 을 톡해 μ‹€μ œ DB ν…Œμ΄λΈ”κ³Ό λ§€ν•‘λ˜λŠ” 객체닀. Entity 라고 ν•˜λ©°, @Entity μ–΄λ…Έν…Œμ΄μ…˜μ„ λΆ™μ—¬μ£Όλ©΄ λœλ‹€. 참고둜 이 μ±…μ—μ„œλŠ” 이후 λ¦¬νŒ©ν† λ§, λ‹€λ₯Έ 툴둜 λ³€ν™˜ 등을 μ‰½κ²Œ ν•˜κΈ° μœ„ν•΄ μ£Όμš” μ–΄λ…Έν…Œμ΄μ…˜μ„ ν΄λž˜μŠ€μ™€ κ°€κΉŒμ΄ λ‘”λ‹€κ³  ν•œλ‹€. lombok 은 μ½”λ“œλ₯Ό λ‹¨μˆœν™” μ‹œμΌœμ£Όμ§€λ§Œ ν•„μˆ˜ μ–΄λ…Έν…Œμ΄μ…˜μ€ μ•„λ‹ˆλΌ μœ„μͺ½μ— λ‘μ—ˆλ‹€. 각 μ–΄λ…Έν…Œμ΄μ…˜ μ„€λͺ…은 μ£Όμ„μœΌλ‘œ λ‹¬μ•„λ‘μ—ˆλ‹€.

 

참고둜 Entity ν΄λž˜μŠ€λŠ” Setter λ©”μ†Œλ“œ μž‘μ„±μ„ μ§€μ–‘ν•œλ‹€. setter μ‚¬μš©μœΌλ‘œ 데이터가 μ—¬κΈ°μ €κΈ°μ„œ λ³€κ²½λ˜μ–΄ 일관성이 없어지고, μ™œ λ³€κ²½ν•˜λŠ”μ§€ κ·Έ λͺ©μ μ΄ λΆˆλΆ„λͺ…ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€. κ·Έλž˜μ„œ 초기 ν•„λ“œ 값을 λ„£κΈ° μœ„ν•΄μ„œ μƒμ„±μžλ₯Ό μ΄μš©ν•œλ‹€. λ§Œμ•½ ν•„λ“œκ°’μ΄ λ³€κ²½λ˜μ–΄μ•Ό ν•˜λŠ” 경우 setter κ°€ μ•„λ‹Œ, μ™œ λ³€κ²½ν•˜λŠ”μ§€ λͺ©μ κ³Ό μ˜λ„κ°€ λΆ„λͺ…ν•˜κ²Œ λ“€μ–΄κ°€ μžˆλŠ” λ³„λ„μ˜ λ©”μ†Œλ“œλ₯Ό μΆ”κ°€ν•œλ‹€.

 

μ—¬κΈ°μ„œλŠ” μƒμ„±μž λŒ€μ‹  λΉŒλ” 클래슀λ₯Ό μ΄μš©ν–ˆλ‹€. μƒμ„±μžλŠ” ν•„λ“œμ˜ μˆœμ„œλ₯Ό 바꾸어도 였λ₯˜κ°€ λ‚˜μ§€ μ•Šμ§€λ§Œ, λΉŒλ” νŒ¨ν„΄μ˜ 경우 builder().filed1(value).filed2(value) 이런 μ‹μœΌλ‘œ μž‘μ„±ν•˜κΈ° λ•Œλ¬Έμ— μ–΄λ–€ ν•„λ“œμ— μ–΄λ–€ 값을 λ„£μ—ˆλŠ”μ§€ λͺ…ν™•ν•˜κ²Œ 인지할 수 있기 λ•Œλ¬Έμ΄λ‹€.

 

package com.study.springbootaws.domain.posts;

import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Getter
@NoArgsConstructor	//lombok. κΈ°λ³Έ μƒμ„±μž 생성
@Entity	//JPA. ν…Œμ΄λΈ”κ³Ό 링크될 ν΄λž˜μŠ€μž„μ„ λ‚˜νƒ€λƒ„
public class Posts {
    @Id	//PK
    @GeneratedValue(strategy = GenerationType.IDENTITY)	//auto_increment
    private Long id;

    @Column(length = 500, nullable = false)	//table 의 column μ„€μ •. ꡳ이 μ–΄λ…Έν…Œμ΄μ…˜ μ•ˆ 달아도 λ‚˜λ¨Έμ§€ ν•„λ“œλ“€ μžλ™μœΌλ‘œ coloumn 됨. λ³„λ„μ˜ μ„€μ • μžˆμ„ 경우 μ„ μ–Έ
    private String title;

    @Column(columnDefinition = "TEXT", nullable = false)
    private String content;

    private String author;

    @Builder	//lombok. λΉŒλ” νŒ¨ν„΄ 클래슀 생성. μ΄λ ‡κ²Œ μƒμ„±μžμͺ½μ— λΆ™μ—¬μ£Όλ©΄ μƒμ„±μž ν•„λ“œλ§Œ λΉŒλ”μ— 포함됨
    public Posts(String title, String content, String author) {
        this.title = title;
        this.content = content;
        this.author = author;
    }
}

 

src/main/java/com.study.springbootsaws/domain/posts/PostsRepository

Repository λŠ” MyBatis μ—μ„œ DAO 와 같은 DB Layer μ ‘κ·Ό ν΄λž˜μŠ€λ‹€. JPAμ—μ„œλŠ” μ–΄λ…Έν…Œμ΄μ…˜ 달 ν•„μš” 없이, λ°‘μ—μ²˜λŸΌ JpaRepository λ₯Ό μƒμ†λ°›λŠ” interface λ₯Ό μƒμ„±ν•˜λ©΄ 기본적인 CRUD λ©”μ†Œλ“œκ°€ μžλ™μœΌλ‘œ μƒμ„±λœλ‹€. 단, Entity 와 Repository ν΄λž˜μŠ€λŠ” λ°€μ ‘ν•˜κ²Œ κ΄€κ³„μžˆμœΌλ―€λ‘œ 같은 μœ„μΉ˜μ— μžˆμ–΄μ•Ό ν•œλ‹€. 이후 νŒ¨ν‚€μ§€ μˆ˜μ • μ‹œ 같이 움직인닀...

package com.study.springbootaws.domain.posts;

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

public interface PostsRepository extends JpaRepository<Posts, Long> {
}

 

Spring Data JPA ν…ŒμŠ€νŠΈ μ½”λ“œ μž‘μ„±ν•˜κΈ°

src/test/java/com.study.springbootaws/domain/posts/PostsRepositoryTest

μœ„ κ²½λ‘œμ— ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό μž‘μ„±ν•΄μ€€λ‹€. JPA 의 save, findAll λ“±μ˜ κΈ°λŠ₯을 ν…ŒμŠ€νŠΈν•  수 μžˆλ‹€. save λŠ” insert/update 에 ν•΄λ‹Ήν•˜λŠ” λ©”μ†Œλ“œκ³  findAll 은 select 에 ν•΄λ‹Ήν•˜λŠ” λ©”μ†Œλ“œλ‹€. 참고둜 μ±…μ—μ„œλŠ” @After μ–΄λ…Έν…Œμ΄μ…˜μ„ μ‚¬μš©ν–ˆμœΌλ‚˜, 버전이 달라지며 @AfterEach λ₯Ό λΆ™μ—¬μ€€λ‹€.

package com.study.springbootaws.domain.posts;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;


import java.util.List;

@SpringBootTest
public class PostRepositoryTest {
    @Autowired
    PostsRepository postsRepository;

    @AfterEach	//@Test μ–΄λ…Έν…Œμ΄μ…˜μ΄ 뢙은 λ©”μ†Œλ“œκ°€ 각각 μ‹€ν–‰λœ ν›„ μžλ™μœΌλ‘œ 싀행됨
    public void cleanup() {
        postsRepository.deleteAll();
    }

    @Test
    public void saveAndReadPosts() {
        String title = "ν…ŒμŠ€νŠΈ κ²Œμ‹œκΈ€";
        String content = "ν…ŒμŠ€νŠΈ λ³Έλ¬Έ";

        postsRepository.save(Posts.builder().title(title).content(content).author("dev@gmail.com").build());

        List<Posts> postsList = postsRepository.findAll();

        Posts posts = postsList.get(0);

        assertThat(posts.getTitle()).isEqualTo(title);
        assertThat(posts.getContent()).isEqualTo(content);
    }
}

 

src/main/resources/application.properties

μœ„ κ²½λ‘œμ— properties νŒŒμΌμ„ λ§Œλ“€μ–΄μ£Όκ³  μ•„λž˜ λ‚΄μš©μ„ λ„£λŠ”λ‹€. 버전이 달라지며 μ½”λ“œλ„ 살짝 λ‹¬λΌμ‘Œλ‹€. μ΄λ ‡κ²Œ ν•˜λ©΄ JPA κ°€ μ‹€ν–‰λ˜λ©° μƒμ„±λœ 쿼리문을 ν„°λ―Έλ„μ—μ„œ 좜λ ₯ν•΄μ€€λ‹€. 또 μ§€κΈˆ μ‚¬μš©ν•˜λŠ” DB λŠ” H2 DB 라 H2 λ¬Έλ²•μœΌλ‘œ 보일텐데, μ•„λž˜μ²˜λŸΌ μ˜΅μ…˜μ„ λ„£μœΌλ©΄ MySql λ¬Έλ²•μœΌλ‘œ λ³€ν™˜λΌμ„œ 보여쀀닀.

spring.jpa.show_sql=true
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

μ•„λž˜μ²˜λŸΌ λœ¬λ‹€!!

λ°˜μ‘ν˜•