[Spring] 스프링 입문 - 스프링 DB 접근 기술 (1)

희원·2022년 1월 17일
0

스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 by 김영한

스프링 DB 접근 기술 (1)

  • H2 데이터베이스 설치
  • 순수 Jdbc
  • 스프링 통합 테스트
  • 스프링 JdbcTemplate
  • JPA
  • 스프링 데이터 JPA

1. H2 데이터베이스 설치

1-1) h2database 설치 (1.4.200 버전)

H2는 개발이나 테스트 용도로 가볍고 편리한 DB로, 웹 화면을 제공한다.

  • 권한 주기: chmod 755 h2.sh (윈도우 사용자는 x)
  • 실행: ./h2.sh (윈도우 사용자는 h2.bat)
  • 데이터베이스 파일 생성 방법
    - jdbc:h2:~/test (최초 한번)
    - ~/test.mv.db 파일 생성 확인
    - 이후부터는 jdbc:h2:tcp://localhost/~/test 이렇게 접속

1-2) 테이블 생성하기

H2 데이터베이스에 접근해서 member 테이블 생성
테이블 관리를 위해 프로젝트 루트에 sql/ddl.sql 파일을 생성

drop table if exists member CASCADE;
create table member
(
 id bigint generated by default as identity,
 name varchar(255),
 primary key (id)
);

H2 데이터베이스가 정상 생성되지 않을 때

다음과 같은 오류 메시지가 나오며 H2 데이터베이스가 정상 생성되지 않는 경우가 있다.

해결방안은 다음과 같다.

  1. H2 데이터베이스를 종료하고, 다시 시작한다.
  2. 웹 브라우저가 자동 실행되면 주소창에 다음과 같이 되어있다.(100.1.2.3이 아니라 임의의 숫자가 나온다.)
  3. 다음과 같이 앞 부분만 100.1.2.3 -> localhost 로 변경하고 Enter를 입력한다. 나머지 부분은 절대 변경하면 안된다. (특히 뒤에 세션 부분이 변경되면 안된다.)
  4. 데이터베이스 파일을 생성하면( jdbc:h2:~/test ), 데이터베이스가 정상 생성된다.

2. 순수 Jdbc를 이용한 DB 연결

2-1) 환경 설정

build.gradle 파일에 jdbc, h2 데이터베이스 관련 라이브러리 추가

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

스프링 부트 데이터베이스 연결 설정 추가

resources/application.properties

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 공백 주의, 공백은 모두 제거해야 한다.


2-2) Jdbc 리포지토리 구현

이전의 Memory 구현체와는 어떤 차이?
MemoryMemberRepository : MemberRepository 인터페이스를 메모리에 구현
JdbcMemberRepository : DB와 연결해서 Jdbc에 구현

ex. 회원을 저장하는 save() 메서드

public class JdbcMemberRepository implements MemberRepository {

   private final DataSource dataSource;
 
   // 생성자가 하나이면 @Autowired 생략 가능
   public JdbcMemberRepository(DataSource dataSource) {
     this.dataSource = dataSource;
   }
 
   @Override
   public Member save(Member member) {
   
     String sql = "insert into member(name) values(?)";
     Connection conn = null;
     PreparedStatement pstmt = null;
     ResultSet rs = null;
     
     try {
     
       conn = getConnection();
       pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
       pstmt.setString(1, member.getName());
       pstmt.executeUpdate();
       rs = pstmt.getGeneratedKeys();
       
       if (rs.next()) {
         member.setId(rs.getLong(1));
       } else {
         throw new SQLException("id 조회 실패");
       }
       
       return member;
       
     } catch (Exception e) {
       throw new IllegalStateException(e);
     } finally {
       close(conn, pstmt, rs);
     }
   }
   ...
}

스프링 설정 변경

     @Bean
     public MemberRepository memberRepository() {
//      return new MemoryMemberRepository();
        return new JdbcMemberRepository(dataSource);
     }
  • DataSource는 데이터베이스 커넥션을 획득할 때 사용하는 객체다. 스프링 부트는 데이터베이스 커넥션 정보를 바탕으로 DataSource를 생성하고 스프링 빈으로 만들어둔다. 그래서 DI를 받을 수 있다.

  • 개방-폐쇄 원칙(OCP, Open-Closed Principle) : 확장에는 열려있고, 수정, 변경에는 닫혀있다.
  • 스프링의 DI (Dependencies Injection)을 사용하면 기존 코드를 전혀 손대지 않고, 설정만으로 구현 클래스를 변경할 수 있다.
  • 회원을 등록하고 DB에 결과가 잘 입력되는지 확인하자.
  • 데이터를 DB에 저장하므로 스프링 서버를 다시 실행해도 데이터가 안전하게 저장된다.

3. 스프링 통합 테스트

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

회원 서비스 스프링 통합 테스트

@SpringBootTest
@Transactional
class MemberServiceIntegrationTest {

   @Autowired MemberService memberService;
   @Autowired MemberRepository memberRepository;
 
   @Test
   public void 회원가입() throws Exception {...}
 
   @Test
   public void 중복_회원_예외() throws Exception {...}
 
}
  • @SpringBootTest : 스프링 컨테이너와 테스트를 함께 실행한다.
  • @Transactional : 테스트 케이스에 이 애노테이션이 있으면, 테스트 시작 전에 트랜잭션을 시작하고, 테스트 완료 후에 항상 롤백한다. 이렇게 하면 DB에 데이터가 남지 않으므로 다음 테스트에 영향을 주지 않는다.
  • @Rollback : 테스트 완료 후에도 DB에 데이터가 남아있는 걸 확인해보고 싶다면 실행할 테스트 클래스 또는 메서드에 @Rollback (org.springframework.test.annotation.Rollback) 애노테이션을 붙이고 value에 false 값을 주면 된다. ( @Rollback(false) )

4. 스프링 JdbcTemplate을 이용한 DB 연결

4-1) 순수 Jdbc와 동일한 환경설정을 하면 된다.

스프링 JdbcTemplate과 MyBatis 같은 라이브러리는 JDBC API에서 본 반복 코드를 대부분 제거해준다. 하지만 SQL은 직접 작성해야 한다.


4-2) JdbcTemplate 리포지토리 구현

ex. 회원을 저장하는 save() 메서드

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

JdbcTemplate을 사용하도록 스프링 설정 변경

     @Bean
     public MemberRepository memberRepository() {
//      return new MemoryMemberRepository();
//      return new JdbcMemberRepository(dataSource);
        return new JdbcTemplateMemberRepository(dataSource);
     }
profile
모든 시작은 사소함으로부터

0개의 댓글