스프링 DB접근기술

김슬기·2022년 9월 30일
0
post-thumbnail

H2 데이터베이스

  • 개발이나 테스트용도로 가볍고 편리한 DB, 웹화면을 제공해준다.
  • 테이블 생성하기

  • 연결을 누르면
  • JDBC URL에 jdbc:h2:tcp://localhost/~/test 이것 입력 후 연결하기
  • 이렇게해야 충돌이 나지 않는다 (소켓을 통해 접근- 여러군데에서 접근 가능
  • 테이블 생성하기
drop table if exists member CASCADE;
create table member
(
id   bigint generated by default as identity, // 자바에선 Long이 여기선 bigint
name varchar(255),                  
primary key (id)
);
  • select *from member; 입력 시 생성된 테이블 보기 가능
  • insert into member(name) values('spring1') 이런식으로 값 추가 가능
  • 이 친구도 역시 서버를 끄면 초기화

순수 JDBC

  • 이 친구는 너무 오래된 기술이라 편하게 강의만 듣기

  • 자바는 DB에 접근하려면 JDBC드라이버가 필요하다.

  • 임폴트가 안되면 스프링 안에 좌측에 있는 코끼리 누르기~~

  • 결국 이론적으로 보면 데이타소스에 getConnection()으로 연결 한 다음 쿼리문을 통해 DB 에 저장하는 방식으로 진행한다.

  • 결국, 쿼리문을 알아야함 (SQL문)

  • 사실 이미 다 발전되었기 때문에 이 친구는 공부할 필요성이 적다.

  • 스프링의 장점??

    • 객체지향의 다형성에서 온다
    • 코드를 분할하여 객체단위로 짜놨기때문에 수정해야할 일이 생기면 그 부분에 대해서만 수정하게되었을때 다른 객체에 영향을 많이 주지않는다.
    • 유지보수에 용이
    • 개방-폐쇄원칙
      • 확장에는 열려있고, 수정에는 닫혀있다.
    • 스프링의 DI(Dependencies Injection)을 이용하면 기존코드를 전혀 손대지않고, 설정만으로 구현클래스 변경 가능
    • 데이터를 DB에 저장하므로 스프링서버를 다시 실행해도 데이터가 안전하게 저장

스프링 통합 테스트

  • 그전 테스트는 순수 자바코드로인한 테스트 >> 대신 속도가 매우빠르다.
  • DB안에 값은 확인이 불가능한 코드였다.
  • 테스트도 스프링과 엮어서 가능
  • 순수 자바코드 테스트가 좀 많이 좋은 테스트일 확률이 높다.
  • @BeforeEach, @AfterEach는 지워준다.
    • 이것들은 자바코드로 테스트할때 리포지를 비워주기 위함이기때문
    • 그것을 @Transactional이 해준다.
      • 이녀석은 테스트를 돌릴 때 저장되는 값들을 롤백 해주는 기능을 한다.
  • 테스트에 @Commit을 입력하면 실제 DB에 적용된다.
  • 추가로 @SpringBootTest를 넣어줘서 스프링 컨테이너와 테스트를 함께 실행
package hello.hellospring.service;

import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import org.assertj.core.api.Assert;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.IllegalFormatException;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
@Transactional
class MemberServiceIntegrationTest {

    @Autowired MemberService memberService;
    @Autowired MemberRepository memberRepository;

    @Test
    void 회원가입() {// 테스트는 한글로 적어도 된다.
        // given,when,then 문법 (주석)(어떤것이 주어졌는데 (이런데이터를 기반으로 하는구나) = given, 이것을 실행했을때(검증하려는 것) = when, 결과가 이게 나와야해 =then)
        Member member = new Member();
        member.setName("hello");
        // when
        Long saveId = memberService.join(member);

        //then
        Member findMember = memberService.findOne(saveId).get();
        Assertions.assertThat(member.getName()).isEqualTo(findMember.getName());
    }

    @Test
    public void 중복_회원_예외() {
        //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));// 뒤에있는 로직을 실행을 할건데, 앞에있는 오류가 터져야한다.
        Assertions.assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
//        try {
//            memberService.join(member2);
//            fail();
//        } catch (IllegalStateException e){
//            Assertions.assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
//        }
        //then

    }

}

JPA

  • 기존의 반복코드는 물론이고, 기본적인 SQL도 JPA 가 직접 만들어서 실행해준다.
  • JPA를 사용하면, SQL과 데이터 중심의 설계에서 객체 중심의 설계로 패러다임 전환 가능
    • SQL문을 쓰지 않으니 그냥 객체 자체를 다룬다고 생각
  • JPA를 사용하면 개발 생산성을 크게 높일 수 있다.
  • 최근 JPA가 굉장히 뜨고 있고, 가장 좋다.
  • ORM(객체와 관계형 데이터베이스를 맵핑) — 장고도 이런 매커니즘
    • 애노테이션으로 맵핑해줌
  • build.gradle에 설정
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.h2database:h2'
  • applicationProperties 에 추가
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
  • Entity 맵핑 해주기
    • domain/Member에 멤버 클래스 위에 @Entity 애노테이션 추가

    • @Id 는 이친구를 pk로 사용하겠다는 애노테이션

    • @GeneratedValue(strategy = GenerationType.*IDENTITY*) 는 DB가 프라이머리 키를 자동으로 생성해준다 라는 의미. 없으면 생성 x

    • 속성값 찾는 방법
      - ex) username이라는 속성을 찾을 때
      - @Column(name = “username”)

      package hello.hellospring.domain;
      
      import javax.persistence.Entity;
      import javax.persistence.GeneratedValue;
      import javax.persistence.GenerationType;
      import javax.persistence.Id;
      
      @Entity
      public class Member {
      
          @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
          private Long id;
          private String name;
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          public Long getId() {
              return id;
          }
      
          public void setId(Long id) {
              this.id = id;
          }
      }
      
  • Jpa에서는 EntityManager이라는 클래스를 사용해서 모든 걸 동작함
  • 또한 JPA를 사용하기위해선 @Transactional이 필요하다 (서비스에 작성)
    • MemberService에 작성
  • SpringConfig에 레포 바꾸기
    @Configuration
    public class SpringConfig {
    
        private EntityManager em;
    
        public SpringConfig(EntityManager em) {
            this.em = em;
        }
    
        @Bean
        public MemberService memberService(){
            return new MemberService(memberRepository());
        }
        @Bean
        public MemberRepository memberRepository(){
    
            return new JpaMemberRepository(em);
        }
    }
  • Repository/JpaMemberRepository생성

public class JpaMemberRepository implements  MemberRepository{

    private final EntityManager em;

    public JpaMemberRepository(EntityManager em) {
        this.em = em;
    }

    @Override
    public Member save(Member member) {
        em.persist(member); // .persist ==> 영구 저장하다.
        return member;
    }

    @Override
    public Optional<Member> findById(Long id) {
        Member member = em.find(Member.class, id); // id로 멤버객체 찾은 후 반환
        return Optional.ofNullable(member);
    }

    @Override
    public Optional<Member> findByName(String name) {
        List<Member> result = em.createQuery("select m from Member as m where m.name", Member.class)
                .setParameter("name",name).getResultList(); // 저 문장을 sql로 바꿔서 실행 - > 이름으로 찾은 리스트
        return Optional.empty();
    }

    @Override
    public List<Member> findAll() {
        List<Member> result = em.createQuery("select m from Member as m", Member.class)
                .getResultList(); // 저 문장을 sql로 바꿔서 실행 ->전체 리스트
        return result;
    }

    @Override
    public void clearStore() {

    }
}
  • 실무에서 사용하기위해선 JPA를 스프링만큼 공부해야함
  • 어렵고 양이 무척많다.
  • 영한선생님 강의로 해보기~~~

스프링 데이터 JPA

  • 이 JPA에 스프링을 더해버린다~~
  • 스프링부트 & JPA만 사용해도 개발생산성 매우 증가
  • 그리고 코드도 확 준다.
  • 하지만 여기에 스프링 데이터 JPA를 사용하면????
  • 리포지토리에 구현 클래스 없이 인터페이스 만으로 개발을 완료가능
  • 그리고 기본 CRUD기능도 스프링데이터 JPA가 제공
  • 필수다 필수~
  • JPA를 알아야지만 사용가능
  • JPA설정을 그대로 사용
  • repository/SpringDataJpaMemberRepository를 인터페이스로 만들기
package hello.hellospring.repository;

import hello.hellospring.domain.Member;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface SpringDataJpaMemberRepository extends JpaRepository<Member, Long>, MemberRepository {
    @Override
    Optional<Member> findByName(String name);
}
  • 이러면 끝이다`~~ 너무간단해
  • extends JpaRepository<Member, Long>이러면 내가 스프링빈에 등록하지 않아도 자동으로 스프링이 구현체를 만들어서스프링 빈에 저친구를 등록해준다.
  • 그것을 가져오는것이 SpringConfig에 이렇게만 적어주면 끝난다.

@Configuration
public class SpringConfig {

   private final MemberRepository memberRepository;

   @Autowired
    public SpringConfig(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    @Bean
    public MemberService memberService(){
        return new MemberService(memberRepository);
    }
}
  • 오토와이어드하면서 멤버레포지를 찾는데 그것이 위에 적어놓은 인터페이스안에 있는코드를 찾아서 가져온다.(그걸 JpaRepository<Member, Long>이녀석이 해준다. 자동구현)

  • 공통클래스로 제공가능한 것들은 스프링 데이터 JPA에서 제공해주지만
    • 인터페이스를 통한 기본적인 CRUD
    • 페이징 기능 자동 제공
  • 우리가 개인적으로 만든 멤버에 접근하는것들은 직접구현해야함
    • 하지만 findByName(),findByEmail() 등등 일정한 규칙을 가지는 이름의 메서드들은 구현하지않고 이름만 선언해 놓아도 사용 가능
profile
낭만그리고김슬기

0개의 댓글