๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

Spring๐Ÿƒ/๊น€์˜ํ•œ์˜ ์Šคํ”„๋ง ์™„์ „์ •๋ณต

์Šคํ”„๋ง ์ž…๋ฌธ ๊ฐ•์˜ ๋…ธํŠธ ์ •๋ฆฌ (5) : ์Šคํ”„๋ง DB ์ ‘๊ทผ ๊ธฐ์ˆ 

728x90

์Šคํ”„๋ง DB ์ ‘๊ทผ ๊ธฐ์ˆ 

1) H2 ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค์น˜
์œˆ๋„์šฐ ์‹คํ–‰ : h2.bat
์ฝ˜์†”์—์„œ test.mv.db ํŒŒ์ผ ์ƒ์„ฑํ•˜๊ธฐ
์ ‘์† ์‹œ jdbc:h2:tcp://localhost/~/test
 
ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ์— sql/ddl.sql ํŒŒ์ผ๋กœ sql ๋ช…๋ น์–ด ๊ด€๋ฆฌํ•˜๋ฉด ํŽธ๋ฆฌํ•˜๋‹ค.
 
2) ์ˆœ์ˆ˜ JDBC
build.gradle ํŒŒ์ผ์— jdbc, h2 ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ด€๋ จ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ถ”๊ฐ€

implementation 'org.springframework.boot:spring-boot-starter-jdbc'
runtimeOnly 'com.h2database:h2'

์Šคํ”„๋ง ๋ถ€ํŠธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ์„ค์ • ์ถ”๊ฐ€

spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa

์ฃผ์˜!: ์Šคํ”„๋ง๋ถ€ํŠธ 2.4๋ถ€ํ„ฐ๋Š” spring.datasource.username=sa ๋ฅผ ๊ผญ ์ถ”๊ฐ€ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด Wrong user name or password ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ์ฐธ๊ณ ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋งˆ์ง€๋ง‰์— ๊ณต๋ฐฑ์ด ๋“ค์–ด๊ฐ€๋ฉด ๊ฐ™์€ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. spring.datasource.username=sa ๊ณต๋ฐฑ ์ฃผ์˜, ๊ณต๋ฐฑ์€ ๋ชจ๋‘ ์ œ๊ฑฐํ•ด์•ผ ํ•œ๋‹ค.
 
JdbcMemberRepository save ๋ฉ”์„œ๋“œ ์Šˆ๋„์ฝ”๋“œ

@Override
  public Member save(Member member) {
    String sql = "insert into member(name) values(?)";

    Connection connection = dateSource.getConnection();

    PreparedStatement pstmt = connection.prepareStatement(sql);
    pstmt.setString(1, member.getName());

    pstmt.executeUpdate();

    return null;
  }

 
Jdbc๋ฅผ ์‚ฌ์šฉํ•œ ์ฝ”๋“œ ๋„ˆ๋ฌด ๊ธธ์–ด์„œ ์ •๋ฆฌ์—์„œ ์ƒ๋žต
*๊ฐ์ฒด์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ : ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์—์„œ ๋‹คํ˜•์„ฑ์„ ์ง€์›ํ•ด์ฃผ๋ฏ€๋กœ ์˜์กด์„ฑ ์ฃผ์ž… ๊ฐ€๋Šฅ
์–ด์…ˆ๋ธ”๋ฆฌ ์ฝ”๋“œ๋งŒ ์ˆ˜์ •ํ•ด๋„ ๋ชจ๋‘ ๋ณ€๊ฒฝ๋œ๋‹ค.
DataSource๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปค๋„ฅ์…˜์„ ํš๋“ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๊ฐ์ฒด๋‹ค. ์Šคํ”„๋ง ๋ถ€ํŠธ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปค๋„ฅ์…˜ ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ DataSource๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋งŒ๋“ค์–ด๋‘”๋‹ค. ๊ทธ๋ž˜์„œ DI๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.
~SOLID ์ค‘ OCP, ๊ฐœ๋ฐฉ ํ์‡„ ์›์น™ : DI
 
์Šคํ”„๋ง ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ

package hello.hellospring.service;
import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemberRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@SpringBootTest
@Transactional
class MemberServiceIntegrationTest {
 @Autowired MemberService memberService;
 @Autowired MemberRepository memberRepository;
 @Test
 public void ํšŒ์›๊ฐ€์ž…() throws Exception {
 //Given
 Member member = new Member();
 member.setName("hello");
 //When
 Long saveId = memberService.join(member);
 //Then
 Member findMember = memberRepository.findById(saveId).get();
 assertEquals(member.getName(), findMember.getName());
 }
 @Test
 public void ์ค‘๋ณต_ํšŒ์›_์˜ˆ์™ธ() throws Exception {
 //Given
 Member member1 = new Member();
 member1.setName("spring");
 Member member2 = new Member();
 member2.setName("spring");
 //When
 memberService.join(member1);
 IllegalStateException e = assertThrows(IllegalStateException.class,
 () -> memberService.join(member2));//์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•ด์•ผ ํ•œ๋‹ค.
 assertThat(e.getMessage()).isEqualTo("์ด๋ฏธ ์กด์žฌํ•˜๋Š” ํšŒ์›์ž…๋‹ˆ๋‹ค.");
 }
}

@SpringBootTest : ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์™€ ํ…Œ์ŠคํŠธ๋ฅผ ํ•จ๊ป˜ ์‹คํ–‰ํ•œ๋‹ค.
@Transactional : ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค์— ์ด ์–ด๋…ธํ…Œ์ด์…˜์ด ์žˆ์œผ๋ฉด, ํ…Œ์ŠคํŠธ ์‹œ์ž‘ ์ „์— ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘ํ•˜๊ณ , ํ…Œ์ŠคํŠธ ์™„๋ฃŒ ํ›„์— ํ•ญ์ƒ ๋กค๋ฐฑํ•œ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด DB์— ๋ฐ์ดํ„ฐ๊ฐ€ ๋‚จ์ง€ ์•Š์œผ๋ฏ€๋กœ ๋‹ค์Œ ํ…Œ์ŠคํŠธ์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๋Š”๋‹ค
๋‹จ์œ„ํ…Œ์ŠคํŠธ๊ฐ€ ํ†ตํ•ฉํ…Œ์ŠคํŠธ๋ณด๋‹ค ๋” ํšจ์œจ์ ์ผ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’๋‹ค
 
Jdbc Templeate
- Mybatis์™€ ์œ ์‚ฌํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ~ ์ˆœ์ˆ˜ Jdbc์—์„œ ๋ฐ˜๋ณต๋˜๋Š” ์ฝ”๋“œ๋ฅผ ๋Œ€๋ถ€๋ถ„ ์ œ๊ฑฐํ•ด์ฃผ์ง€๋งŒ, SQL์€ ์ง์ ‘ ์ž‘์„ฑํ•ด์•ผํ•œ๋‹ค.
์ƒ์„ฑ์ž๊ฐ€ ํ•˜๋‚˜์ธ ๊ฒฝ์šฐ @Autowired ์–ด๋…ธํ…Œ์ด์…˜์„ ํ†ตํ•œ ์˜์กด์„ฑ ์ฃผ์ž…์—์„œ ์–ด๋…ธํ…Œ์ด์…˜์€ ์ƒ๋žต์ด ๊ฐ€๋Šฅํ•˜๋‹ค
- ์™œ Templeate์ธ๊ฐ€? -> ๋””์ž์ธ ํŒจํ„ด ์ค‘ ํ…œํ”Œ๋ฆฟ ๋ฉ”์„œ๋“œ ํŒจํ„ด์„ ๋งŽ์ด ์‚ฌ์šฉํ–ˆ๊ธฐ ๋•Œ๋ฌธ
 
JPA
Jdbc Templeate์—์„œ SQL ์ฟผ๋ฆฌ๋„ JPA๊ฐ€ ์‹คํ–‰ํ•ด์ค€๋‹ค ~ ๊ฐ์ฒด ์ค‘์‹ฌ์˜ ์„ค๊ณ„๋กœ ํŒจ๋Ÿฌ๋‹ค์ž„์„ ์ „ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค
- build.gradle

implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

- application.properties

# show-sql : JPA๊ฐ€ ์ƒ์„ฑํ•˜๋Š” SQL์„ ์ถœ๋ ฅํ•œ๋‹ค
spring.jpa.show-sql=true
# ddl-auto : JPA๋Š” ํ…Œ์ด๋ธ”์„ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š”๋ฐ none์„ ์‚ฌ์šฉํ•˜๋ฉด
# ํ•ด๋‹น ๊ธฐ๋Šฅ์„ ๋ˆ๋‹ค ~ create๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์—”ํ‹ฐํ‹ฐ ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ํ…Œ์ด๋ธ”๋„ ์ง์ ‘ ์ƒ์„ฑํ•ด์ค€๋‹ค
spring.jpa.hibernate.ddl-auto=none

JPA๋Š” ์ธํ„ฐํŽ˜์ด์Šค๊ณ , Hibernate๊ฐ€ ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ตฌํ˜„์ฒด
JPQL : ๊ฐ์ฒด๋ฅผ ๋Œ€์ƒ์œผ๋กœ ์ฟผ๋ฆฌ๋ฅผ ๋‚ ๋ฆฐ๋‹ค

package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import javax.persistence.EntityManager;
import java.util.List;
import java.util.Optional;
public class JpaMemberRepository implements MemberRepository {
 private final EntityManager em;
 public JpaMemberRepository(EntityManager em) {
 this.em = em;
 }
 public Member save(Member member) {
 em.persist(member);
 return member;
 }
 public Optional<Member> findById(Long id) {
 Member member = em.find(Member.class, id);
 return Optional.ofNullable(member);
 }
 public List<Member> findAll() {
 return em.createQuery("select m from Member m", Member.class)
 .getResultList();
 }
 // ๊ฐ์ฒด๋ฅผ ๋Œ€์ƒ์œผ๋กœ ์ฟผ๋ฆฌ๋ฅผ ๋‚ ๋ฆฌ๋Š” JPQL
 public Optional<Member> findByName(String name) {
 List<Member> result = em.createQuery("select m from Member m where
m.name = :name", Member.class)
 .setParameter("name", name)
 .getResultList();
 return result.stream().findAny();
 }
}

JPA๋ฅผ ํ†ตํ•œ ๋ชจ๋“  ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์€ Transactional ๋‹จ์œ„๋กœ ์‹คํ–‰๋˜์–ด์•ผ ํ•œ๋‹ค.
 
Spring Data JPA
์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๋Š” JPA๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๋„๋ก ๋„์™€์ฃผ๋Š” ๊ธฐ์ˆ ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ JPA๋ฅผ ๋จผ์ € ํ•™์Šตํ•œ ํ›„ ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๋ฅผ ํ•™์Šตํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
 
*JPA Repository๋ฅผ ์ƒ์†ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ์ œ๋„ค๋ฆญ์Šค : <์—”ํ‹ฐํ‹ฐ, ์‹๋ณ„์ž ํƒ€์ž…>
+ Spring Data JPA๊ฐ€ JPA Repository๋ฅผ ์ƒ์†ํ•˜๋Š” ๋ ˆํฌ์ง€ํ† ๋ฆฌ์˜ ๊ตฌํ˜„์ฒด๋ฅผ ์ž๋™์œผ๋กœ ์Šคํ”„๋ง ๋นˆ์— ๋“ฑ๋กํ•ด์ค€๋‹ค
๊ตฌํ˜„์ฒด๋ฅผ ๋งŒ๋“ค ๋•Œ ํ”„๋ก์‹œ๋กœ ๋งŒ๋“ ๋‹ค

- Spring Data JPA ์ œ๊ณต ๊ธฐ๋Šฅ

  • ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•œ ๊ธฐ๋ณธ CRUD
  • findByName(), findByEmail() ๋“ฑ ๋ฉ”์„œ๋“œ ์ด๋ฆ„๋งŒ์œผ๋กœ ์กฐํšŒ ๊ธฐ๋Šฅ ์ œ๊ณต
  • ํŽ˜์ด์ง• ๊ธฐ๋Šฅ ์ž๋™ ์ œ๊ณต
package hello.helloSpring.repository;

import hello.helloSpring.domain.Member;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
// ์—”ํ‹ฐํ‹ฐ, ์‹๋ณ„์ž ํƒ€์ž…
public interface SpringDataJpaMemberRepository extends JpaRepository<Member, Long>, MemberRepository {

  // equals JPQL select m from Member m where m.name = ?
  @Override
  Optional<Member> findByName(String name);
}

> ์ฐธ๊ณ : ์‹ค๋ฌด์—์„œ๋Š” JPA์™€ ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ณ , ๋ณต์žกํ•œ ๋™์  ์ฟผ๋ฆฌ๋Š” Querydsl์ด๋ผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. Querydsl์„ ์‚ฌ์šฉํ•˜๋ฉด ์ฟผ๋ฆฌ๋„ ์ž๋ฐ” ์ฝ”๋“œ๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ณ , ๋™์  ์ฟผ๋ฆฌ๋„ ํŽธ๋ฆฌํ•˜๊ฒŒ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ์กฐํ•ฉ์œผ๋กœ ํ•ด๊ฒฐํ•˜๊ธฐ ์–ด๋ ค์šด ์ฟผ๋ฆฌ๋Š” JPA๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋„ค์ดํ‹ฐ๋ธŒ ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, ์•ž์„œ ํ•™์Šตํ•œ ์Šคํ”„๋ง JdbcTemplate๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

728x90