목차
0. 환경
- m1 macbook
- IntelliJ IDEA(m1) - 202102
- java 11(AdoptOpenJDK-11.0.11)
1. JdbcTemplate
- hello/hellospring/repository/JdbcMemberRepository.java
- implementation 'org.springframework.boot:spring-boot-starter-jdbc'를 사용하므로 build.gradle 파일을 수정할 필요는 없습니다.
- MyBatis와 비슷한 라이브러리로 jdbc api의 반복적인 코드를 제거(생산성 향상) 해 줍니다. 그러나 SQL은 직접 작성해야 합니다.
- 디자인 패턴 중 템플릿 메서드 패턴를 주로 활용하였으므로 JdbcTemplate로 이름이 붙여졌습니다.
[순수 Jdbc 리포지토리 구현]
[JdbcTemplate를 활용하여 리포지토리 구현]
개발자는 SQL만 처리하면 됩니다.
[순수 Jdbc 사용 시 개발자가 구현할 부분]
@Override
public Member save(Member member) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = getConnection();
String sql = "insert into member(name) values(?)";
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);
}
}
private Connection getConnection() {
return DataSourceUtils.getConnection(dataSource);
}
private void close(Connection conn, PreparedStatement pstmt, ResultSet rs) {
try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (pstmt != null) {
pstmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (conn != null) {
close(conn);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
private void close(Connection conn) throws SQLException {
DataSourceUtils.releaseConnection(conn, dataSource);
}
[JdbcTemplate를 사용 시 개발자가 구현할 부분]
String sql = "insert into member(name) values(?)";
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;
2. JdbcTemplate 리포지토리 구현
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) {
/*
Insert 쿼리를 작성할 필요가 없이 SimpleJdbcInsert를 활용하여,
테이블 이름과, 키 컬럼을 등록한 후
Map<String, Object>에 Insert 할 데이터를 담아 매개변수로 넣어주면 됩니다.
*/
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) {
/*
jdbcTemplate를 이용해 쿼리와 바인드 매개변수를 날리고,
결과를 RowMapper를 통해 매핑을 하고
List로 받아서 Optional로 반환 합니다.
*/
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() {
//기본 형태
//[Option] + [Enter] 를 이용해 람다로 변경 가능합니다. (Replace with lambda)
// return new RowMapper<Member>() {
// @Override
// public Member mapRow(ResultSet rs, int rowNum) throws SQLException {
// Member member = new Member();
// member.setId(rs.getLong("id"));
// member.setName(rs.getString("name"));
// return member;
// }
// };
//람다 형태
//템플릿 메서드 패턴과 콜백을 가지고 구현
return (rs, rowNum) -> {
Member member = new Member();
member.setId(rs.getLong("id"));
member.setName(rs.getString("name"));
return member;
};
}
}
3. 설정 파일 변경
- hello/hellospring/SpringConfig.java
- 스프링 빈 설정 파일
package hello.hellospring;
import hello.hellospring.repository.*;
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.persistence.EntityManager;
import javax.sql.DataSource;
//자바 코드로 직접 스프링 빈 등록하기
@Configuration
public class SpringConfig {
//DataSource는 데이터베이스 커넥션을 획득할 때 사용하는 객체다.
//스프링 부트는 데이터베이스 커넥션 정보를 바탕으로 DataSource를 생성하고 스프링 빈으로 만들어둔다. 그래서 DI를 받을 수 있다.
//@Configuration도 스프링이 관리
//스프링이 설정파일을 보고 알아서 빈을 생성해줍니다.
private final DataSource 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);//순수 JDBC
return new JdbcTemplateMemberRepository(dataSource);// JdbcTemplate
}
}
'Backend > 코드로 배우는 스프링 부트' 카테고리의 다른 글
[코드로 배우는 스프링 부트] 13. 스프링 데이터 JPA 연동 (0) | 2021.11.13 |
---|---|
[코드로 배우는 스프링 부트] 12. JPA 연동 (0) | 2021.11.12 |
[코드로 배우는 스프링 부트] 10. 스프링 통합 테스트 (0) | 2021.11.06 |
[코드로 배우는 스프링 부트] 9. 순수 Jdbc H2DB 연동하기 (0) | 2021.11.05 |
[코드로 배우는 스프링 부트] 8. H2DB 설치 (0) | 2021.11.04 |