가랑비에 옷 젖는 줄 모른다 💻/스프링

[스프링입문]섹션6-스프링 DB 접근 기술

잔뜩 2023. 9. 29. 04:28

H2 데이터베이스 설치

H2 DB 설치 , test.mv.db 파일 생성된거 확인

 

파일 생성된 거 확인 후에는 PDF 강의안에 적힌 순으로 접속하자.

윈도우 라서 h2.bat 실행, 실행된 cmd 창 꺼버리면 db가 죽는다 ㅋㅋ 항상 켜놓고 할 것

 

 

순수 JDBC(편하게 듣기)

 JDBC API로 직접 코딩하는 것은 20년 전 이야기

package hello.hellospring;

import hello.hellospring.repository.JdbcMemberRepository;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import hello.hellospring.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import javax.xml.crypto.Data;

@Configuration
public class SpringConfig {
    private DataSource dataSource;

    @Autowired
    public SpringConfig(DataSource dataSource){
        this.dataSource = dataSource;
    }
    @Bean
    public MemberService memberService(){
        return new MemberService(memberRepository());
    }

    @Bean
    public MemberRepository memberRepository(){
        //return new MemoryMemberRepository();
        return new JdbcMemberRepository(dataSource);
        // 인터페이스. 리턴하는건 구현체
    }

}

DataSource는 데이터베이스 커넥션을 획득할 때 사용하는 객체다. 스프링 부트는 데이터베이스 커넥션
정보를 바탕으로 DataSource를 생성하고 스프링 빈으로 만들어둔다. 그래서 DI를 받을 수 있다.

 

 

 

중요한건 스프링을 왜 쓰냐!

인터페이스를 두고 구현체를 바꿔 쓸 수있는 걸 다형성 활용이라 하는데

스프링은 상당히 편리하게 스프링 컨테이너가 지원해준다.

DI 덕분

 

기존의 코드는 손 안대도, 어플리케이션 설정하는 코드

위와 같은 걸 어셈블리라고 합니다. 어플리케이션 조립

 

이걸 편리하게 해주는게 스프링의 장점입니다.

 

 

OCP  -> 갈아 끼우는 건 자유, 기존 코드 변경 하진 않음 .. 이런 맥락인가

 

객체지향의 매력은 상속보단 OCP가 참 큰 장점이다!

 

 

스프링 통합 테스트

** 가급적이면 단위테스트가 훨씬 좋습니다.

컨테이너까지 올려야 되는 상황이라면 테스트설계가 잘못됐을 확률이 높습니다~

 

스프링 컨테이너와 DB까지 연결한 통합 테스트를 진행해보자.

@SpringBootTest : 스프링 컨테이너와 테스트를 함께 실행한다.
@Transactional : 테스트 케이스에 이 애노테이션이 있으면, 테스트 시작 전에 트랜잭션을 시작하고, 
테스트 완료 후에 항상 롤백한다. 이렇게 하면 DB에 데이터가 남지 않으므로 다음 테스트에 영향을 주지
않는다. DB 실제 데이터에 반영을 안하다! 지우는게 아니라

 

이제는 스프링컨테이너한테 멤버서비스, 멤버 리퍼지토리 내놔! 해야합니다.

생성자로 인젝션 하는게 아니라 Autowired 하자!

 

Autowired 해주기 때문에 BeforeEach 없어도 되고

@Transactional 때문에 다음 테스트에 영향이 없어서 After~ 이것도 없어도 됌

 

** 회원 가입 테스트 돌렸을 때 트랜잭셔널 애노테이션이 있으면 DB에 안남고 , 주석하면 남은 걸 볼 수있었다.

스프링 JdbcTemplate

**이렇게 하는구나 정도만 압시다.

- 왜 템플릿이냐면 , 디자인패턴중에 템플릿메서드패턴이라는게 있어서~

- 생성자가 한개인 경우에는 Autowired 생략가능

package hello.hellospring.repository;

import hello.hellospring.domain.Member;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;
import java.util.List;
import java.util.Optional;

public class JdbcTemplateMemberRepository implements MemberRepository{
    private final JdbcTemplate jdbcTemplate;

    @Autowired // 생성자가 한개인 경우엔 autowired 생략가능하다
    public JdbcTemplateMemberRepository(DataSource dataSource){
        jdbcTemplate = new JdbcTemplate(dataSource);
    }
    @Override
    public Member save(Member member) {
        return null;
    }

    @Override
    public Optional<Member> findById(Long id) {
        return Optional.empty();
    }

    @Override
    public Optional<Member> findByName(String name) {
        return Optional.empty();
    }

    @Override
    public List<Member> findAll() {
        return null;
    }
}

완성본

package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class JdbcTemplateMemberRepository implements MemberRepository {
 private final JdbcTemplate jdbcTemplate;
 public JdbcTemplateMemberRepository(DataSource dataSource) {
 jdbcTemplate = new JdbcTemplate(dataSource);
 }
 @Override
 public Member save(Member member) {
 SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate);
 jdbcInsert.withTableName("member").usingGeneratedKeyColumns("id"); Map<String, Object> parameters = new HashMap<>();
 parameters.put("name", member.getName());
 Number key = jdbcInsert.executeAndReturnKey(new
MapSqlParameterSource(parameters));
 member.setId(key.longValue());
 return member;
 }
 @Override
 public Optional<Member> findById(Long id) {
 List<Member> result = jdbcTemplate.query("select * from member where id 
= ?", memberRowMapper(), id);
 return result.stream().findAny();
 }
 @Override
 public List<Member> findAll() {
 return jdbcTemplate.query("select * from member", memberRowMapper());
 }
 @Override
 public Optional<Member> findByName(String name) {
 List<Member> result = jdbcTemplate.query("select * from member where 
name = ?", memberRowMapper(), name);
 return result.stream().findAny();
 }
 private RowMapper<Member> memberRowMapper() {
 return (rs, rowNum) -> {
 Member member = new Member();
 member.setId(rs.getLong("id"));
 member.setName(rs.getString("name"));
 return member;
 };
 }
}

JDBC와 비교 했을 때

 

엄청 간결해진 걸 볼 수 있다.

 

Simple Jdbc Insert 는 재밌는게 저걸 이용하면 쿼리를 짤 필요가 없다.

테이블명이랑 pk 있으면 insert 구문을 만들어준다.

요렇게 하는 정도만 알면 됩니다.

 

SpringConfig에 조립해주기

 

JPA


JPA는 기존의 반복 코드는 물론이고, 기본적인 SQL도 JPA가 직접 만들어서 실행해준다.
JPA를 사용하면, SQL과 데이터 중심의 설계에서 객체 중심의 설계로 패러다임을 전환을 할 수 있다.
JPA를 사용하면 개발 생산성을 크게 높일 수 있다.

 

build.gradle 이랑 properties 에 설정 추가하기

Member 도메인에 @Entity 달기  JPA가 관리하는 엔티티다~

DB에 값을 넣으면 ID가 생성되는데 이를 아이덴티티 라고 한다.

 

만일 컬럼 이름이 username이면 위와 같이 매핑해주면 됩니다/

 

위의 정보로 create 를 해주는 거다!

 

JPA member Repository 만들기

jpa는 엔티티 매니저로 동작

data - jpa를 라이브러리로 받아서 , 스프링 부트가 자동으로 엔티티 매니저를 생성해주기때문에

인젝션 받으면 된다.

 

결론: JPA를 쓰려면 이 EntityManager 를 주입받아야 합니다.

persist 뜻이 영속적으로 , 멤버를 저장하겠다.

두번째는 null 이 있을 수도 있으니까!

 

인라인 기능 사용하기..인라인이란?

인라인 기능 써서 바꾸기...

위의 쿼리는 jpql 쿼리 언어인데 , 테이블 대상으로 쿼리를 날리는게 아니라 객체를 대상으로 쿼리를 날린다.

 (멤버 엔티티 대상으로 쿼리를 날린다.)

 

select도 멤버 엔티티 자체를 select 하는 걸 볼 수 있다.

 

JPA 쓰려면 주의해야 할게 항상 transactional 이 있어야 한다.

서비스 계층에 트랜잭션 추가

스프링은 해당 클래스의 메서드를 실행할 때 트랜잭션을 시작하고, 메서드가 정상 종료되면 트랜잭션을
커밋한다. 만약 런타임 예외가 발생하면 롤백한다.
JPA를 통한 모든 데이터 변경은 트랜잭션 안에서 실행해야 한다.

 

SpringConfig 에서 파일 커스텀 해주고 ~

만든 Integration Test에서 회원가입 테스트 해보기! 성공~

쿼리문이 날라 갔음을 확인 해 볼 수 있다. 그리고 inset 까지

id는 자동으로 생성이 된다.

 

확인해 보기 위해서 통합테스트에서 @commit 애노테이션을 달자

DB에 반영 됐음을 알 수 있다.

 

JPA 통합테스트 성공 했음도 확인완~

 

JPA도 스프링 만큼 성숙한 기술이고, 학습해야 할 분량도 방대하다. 다음 강의와 책을 참고하자.
> - 인프런 강의 링크: 인프런 - 자바 ORM 표준 JPA 프로그래밍 - 기본편
> - JPA 책 링크: 자바 ORM 표준 JPA 프로그래밍 - YES24

인텔리제이 자동정렬

스프링 데이터 JPA

반복 개발해온 기본 CRUD 기능도 스프링 데이터 JPA가 모두 제공합니다

 

JPA를 이용해보자, JPA리포지토리 인터페이스 상속받기

멤버 엔티티, id 기본키 타입은 long , 멤버리포지토리도 상속

인터페이스는 다중 상속이 가능하다.

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);
}

인터페이스만 있지만 스프링데이터 JPA가 JpaRepositry를 받고 있으면

SpringDataJpaMemberRepository가 자동으로 구현체를 만들어서 스프링빈에 자동으로 등록합니다.우리는

SpringConfig에서 가져다 쓰기만 하면 된다. 이제 따로 return 해줄게 없다.

스프링 데이터 JPA 회원 리포지토리를 사용하도록 스프링 설정 변경

 

기존

 

 

스프링 JPA 변경 후

 

package hello.hellospring;

import hello.hellospring.repository.*;
import hello.hellospring.service.MemberService;
import jakarta.persistence.EntityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import javax.xml.crypto.Data;

@Configuration
public class SpringConfig {
    private final MemberRepository memberRepository;

    public SpringConfig(MemberRepository memberRepository){
        this.memberRepository = memberRepository;
    }
    @Bean
    public MemberService memberService() {
        return new MemberService(memberRepository);
    }

스프링 데이터 JPA 제공 기능
인터페이스를 통한 기본적인 CRUD
findByName() , findByEmail() 처럼 메서드 이름 만으로 조회 기능 제공
페이징 기능 자동 제공

 

참고: 실무에서는 JPA와 스프링 데이터 JPA를 기본으로 사용하고, 복잡한 동적 쿼리는 Querydsl이라는
라이브러리를 사용하면 된다. Querydsl을 사용하면 쿼리도 자바 코드로 안전하게 작성할 수 있고, 동적
쿼리도 편리하게 작성할 수 있다. 이 조합으로 해결하기 어려운 쿼리는 JPA가 제공하는 네이티브(순수) 쿼리를
사용하거나, 앞서 학습한 스프링 JdbcTemplate를 섞어서사용하면 된다. my batis 도!

728x90