Intro
데이터가 메모리에 저장돼 서버를 껐다 켜면 데이터가 날아가는 현상을 방지하기 위해 JdbcTemplate를 사용할 것이다.
JdbcTemplate를 사용하면 기존 JDBC의 반복 코드를 대부분 제거해 SQL과 결과 처리에 집중할 수 있도록 지원해준다.
JDBCTemplate 적용
JdbcTemplate를 사용하기 위해 gradle 파일에 JDBC를 implement 해줘야 한다.
build.gradle
dependencies {
...
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
...
}
이제 MemberRepository를 구현하는 JdbcTemplateMemberRepository 클래스를 만들고,
JdbcTemplate 변수를 만들어주자.
JdbcTemplateMemberRepository.java
package com.devjaewoo.hellospring.repository;
import com.devjaewoo.hellospring.domain.Member;
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;
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;
}
}
그런데 JdbcTemplate는 DataSource를 필요로 한다.
여기서 우리가 이전에 application.yml에 등록한 DataSource가 쓰인다.
spring:
datasource:
url: jdbc:h2:~/test
driver-class-name: org.h2.Driver
username: sa
이 DataSource는 스프링 컨테이너에서 자동으로 Bean으로 등록시켜주기 때문에, @Autowired로 받기만 하면 된다.
지금 Repository를 Bean으로 등록하는 역할을 SpringConfig 클래스에서 해주고 있으니 SpringConfig의 생성자에 DataSource를 받도록 수정하자.
하는 김에 memberRepository 함수도 MemoryMemberRepository가 아닌
JdbcTemplateMemberRepository를 반환하도록 수정해주자.
SpringConfig.java
package com.devjaewoo.hellospring;
import com.devjaewoo.hellospring.repository.JdbcTemplateMemberRepository;
import com.devjaewoo.hellospring.repository.MemberRepository;
import com.devjaewoo.hellospring.repository.MemoryMemberRepository;
import com.devjaewoo.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;
@Configuration
public class SpringConfig {
private final 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 JdbcTemplateMemberRepository(dataSource);
}
}
이제 JdbcTemplateMemberRepository 클래스에서 JdbcTemplate를 쓸 수 있게 되었다. 완성된 코드는 다음과 같다.
package com.devjaewoo.hellospring.repository;
import com.devjaewoo.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;
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 Optional<Member> findByName(String name) {
List<Member> result = jdbcTemplate.query("select * from member where name = ?", memberRowMapper(), name);
return result.stream().findAny();
}
@Override
public List<Member> findAll() {
return jdbcTemplate.query("select * from member", memberRowMapper());
}
private RowMapper<Member> memberRowMapper() {
return (rs, rowNum) -> {
Member member = new Member();
member.setId(rs.getLong("id"));
member.setName(rs.getString("name"));
return member;
};
}
}
코드를 보면 JdbcTemplate에서 쿼리를 날릴 때 memberRowMapper를 호출해주는 것을 볼 수 있다.
JdbcTemplate는 쿼리의 결과를 ResultSet 형태로 제공하는데, 이 ResultSet을 이용해 내가 받고자 하는 데이터 타입으로 바꿔주는 함수를 만들어야 한다.
JdbcTemplate에서 이 함수를 받아 쿼리 결과를 List <Member>로 반환해주는 원리이다.
위의 코드에선 그 역할을 memberRowMapper 함수가 맡는데, rs와 rowNum을 받아 member를 반환해주는 것을 볼 수 있다.
기능 동작 확인
기능 동작 확인에 앞서 h2 콘솔로 다시 들어가 테이블을 만들어줘야 한다.
h2 콘솔로 들어가 아래의 SQL을 실행해 테이블을 만들어주자.
DROP TABLE IF EXISTS MEMBER;
CREATE TABLE MEMBER (
ID BIGINT GENERATED BY DEFAULT AS IDENTITY,
NAME VARCHAR(255),
PRIMARY KEY(ID)
);
이제 localhost:8080으로 들어가서 회원가입을 해보자.
회원이 정상적으로 저장되고, 서버를 껐다가 켜도 데이터가 남아있는 것을 확인할 수 있다.
다음 시간에는 통합 테스트를 해보도록 하겠다.
출처
'Study > Spring Boot' 카테고리의 다른 글
[Spring Boot] JPA로 JdbcTemplate 대체하기 (0) | 2022.02.15 |
---|---|
[Spring Boot] 스프링 통합 테스트 케이스 만들기 (0) | 2022.02.15 |
[Spring Boot] h2 Database 설정하기 (0) | 2022.02.14 |
[Spring Boot] h2 DB에 TCP 접근 시 SocketTimeoutException 뜰 때 (0) | 2022.02.14 |
[Spring Boot] 회원가입, 회원조회 기능 구현 (0) | 2022.02.13 |