본문 바로가기
Back-End/Spring Boot

예습) 스프링 입문

by newny 2023. 5. 3.
반응형

@Transactional

테스트 케이스에 이 어노테이션이 있으면, 테스트 시작 전에 트랜잭션을 시작하고, 테스트 완료 후에 항상 롤백한다. 이렇게 하면 DB에 데이터가 남지 않으므로 다음 테스트에 영향을 주지 않는다.

 

@SpringBootTest

스프링 컨테이너와 테스트를 함께 실행한다.

 

@Autowired 키워드 생략가능 조건

생성자가 하나일때는 생략 가능

 

JdbcTemplate 클래스

JdbcTemplate은 JDBC 코어 패키지의 중앙 클래스로 JDBC의 사용을 단순화하고 일반적인 오류를 방지하는데 도움이 된다. 개발자가 JDBC를 직접 사용할 때 발생하는 다음과 같은 반복 작업을 대신 처리해준다.

  • 커넥션 획득
  • statement를 준비하고 실행
  • 결과를 반복하도록 루프를 실행
  • 커넥션 종료, statement 및 resultset 종료
  • 트랜잭션을 다루기 위한 커넥션 동기화
  • 예외 발생 시 스프링 예외 변환기 실행

 

DataSource 인터페이스

DB와 관계된 커넥션 정보를 담고 있으며 빈으로 등록하여 인자로 넘겨준다. 이 과정을 통해 Spring은 DataSource로 DB와의 연결을 획득한다.

 

SimpleJdbcInsert

간단하게 데이터를 DB에 저장하기 위해 만들어진 구현체

객체 생성 시 매개값으로 ‘DB와 관계된 커넥션 정보를 담은 DataSource 객체를 매개 값으로 하는’ JdbcTemplate 객체를 넣는다.

 

withTableName() 메소드

insert 하고자 하는 테이블명을 매개값으로 넣는다

 

usingGeneratedKeyColumns() 메소드

데이터베이스에 레코드를 추가할 때 자동으로 생성 된 프라이머리 키 값을 가져오기 위해 사용된다.

 

MapSqlParameterSource 클래스

데이터베이스와 상호 작용할 때 Mapping되어있 정보를 SQL 쿼리의 바인딩 변수에 할당하는 데 사용된다.

 

jdbcTemplate.query() 메소드

첫 번째 인자 : 실행할 쿼리문을 인자로 넣어줌

두 번째 인자 : 조회 결과를 매핑할 RowMapper 가 리턴타입인 메소드를 넣어줌

세 번째 인자 : 바인딩 변수가 한개라면 그에 해당하는 한개의 값을 인자로 넣어줌, 바인딩 변수가 여러개라면 바인딩 변수의 순서에 맞춰 그 값들을 배열에 넣어서 인자로 넣어줌

 

RowMapper<T> 인터페이스

인터페이스 이므로 사용하려면 메소드 오버라이딩을 해줘야한다.

public interface RowMapper<T> {

@Nullable
T mapRow(ResultSet rs, int rowNum) throws SQLException;

}

아래의 코드는 RowMapper 인터페이스를 익명구현하기위해 람다식을 사용하여 메소드를 정의하였다.

인터페이스의 익명 구현 객체를 람다식으로 표현하려면 인터페이스가 단 하나의 추상 메소드만 가져야 한다. (RowMapper 인터페이스는 mapRow 추상메소드 하나만 가지고 있음)

인터페이스가 단 하나의 추상 메소드를 가질 때, 이를 함수형 인터페이스(functional interface)라고 한다.

private RowMapper<Member> memberRowMapper() {
    return (rs, rowNum) -> {
        Member member = new Member();
        member.setId(rs.getLong("id"));
        member.setName(rs.getString("name"));
        return member;
    };
}

 

[코드 뜯기]

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.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

//JdbcTemplate 클래스를 이용하기
public class JdbcTemplateMemberRepository implements MemberRepository {

    // JdbcTemplate를 멤버 변수로 선언 해 놓음
    private final JdbcTemplate jdbcTemplate;

    // JdbcTemplate 사용 시작 (기본 생성자에서 의존성 주입)
    // DB와 관계된 커넥션 정보를 담은 DataSource의 객체를 주입시킨다.
    // 이때 DataSource는 스프링 빈으로 등록되어 있어야 한다.
    public JdbcTemplateMemberRepository(DataSource dataSource) {
        jdbcTemplate = new JdbcTemplate(dataSource);
    }

    // 회원 정보 저장 메소드
    @Override
    public Member save(Member member) {
        // 자동 insert를 가능하게하는 jdbcInsert 객체 생성
        SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate);

        // jdbcInsert 객체에 DB insert에 필요한 정보들을 담는 과정 (쿼리문 따로 작성 하지 않아도 됨)
        jdbcInsert.withTableName("member").usingGeneratedKeyColumns("id");
        Map<String, Object> parameter = new HashMap<>(); // member 객체의 정보를 담을 HashMap 객체
        parameter.put("name", member.getName()); // member 객체의 정보를 컬럼명과 값으로 HashMap 객체에 mapping & 저장

        // insert에 필요한 정보를 담은 jdbcInsert 객체를 parameter를 매개로 받은 executeAndReturnKey 메소드를 통해
        // DB에 insert를 실행시킴과 동시에 Primary key를 반환하게 한다.
        // 반환된 key의 값이 어떤 타입인지 알 수 없기 때문에 Number 타입으로 받는다.
        Number key = jdbcInsert.executeAndReturnKey(new MapSqlParameterSource(parameter));

        // 반환받은 Number 타입의 key값을 long 타입으로 바꾼 후 member객체의 id에 set 해준다.
        member.setId(key.longValue());

        // id와 name이 저장된 member 객체 반환
        return member;
    }

    // 아이디 찾기 메소드
    @Override
    public Optional<Member> findById(Long id) {
        // jdbcTemplate.query() 메소드를 이용
        // 첫번째 인자 : 쿼리문
        // 두번째 인자 : 실행한 쿼리문의 결과를 담는 객체
        // 세번째 인자 : 바인딩변수에 해당하는 값
        List<Member> result = jdbcTemplate.query("select * from member where id = ?", memberRowMapper(), id);
        return result.stream().findAny();
    }

    // 회원 정보 전체 조회 메소드
    @Override
    public List<Member> findAll() {
        // findById() 메소드와 동일한 구조
        return jdbcTemplate.query("select * from member", memberRowMapper());
    }

    // 이름 찾기 메소드
    @Override
    public Optional<Member> findByName(String name) {
        // findById() 메소드와 동일한 구조
        List<Member> result = jdbcTemplate.query("select * from member where name = ?", memberRowMapper(), name);
        return result.stream().findAny();
    }

    // 실행한 쿼리문의 결과를 member 객체에 담는 과정의 메소드
    private RowMapper<Member> memberRowMapper() {
        return (rs, rowNum) -> { //인터페이스 추상메소드 사용을 위해 익명객체 생성
                    Member member = new Member();
                    member.setId(rs.getLong("id"));
                    member.setName(rs.getString("name"));
                    return member;
                };
    }
}
반응형

'Back-End > Spring Boot' 카테고리의 다른 글

예습)스프링 입문  (0) 2023.05.11
예습) 스프링 기초  (0) 2023.04.27
예습) 스프링 기초  (0) 2023.04.25

댓글