Spring boot μ Spring Data JPA μ μ©νκΈ°
νλ‘μ νΈμ 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
μλμ²λΌ λ¬λ€!!