일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- Excel
- Stateless
- di
- js
- dependency injection
- Kotlin
- 라이프 사이클
- HTTP 메서드
- 캐시
- 의존성 주입
- cache
- Spring
- 로그인
- Singleton
- HTTP
- VUE
- vue-cli
- DB
- Security
- Java
- Setter
- thymeleaf
- Vue.js
- 프로토타입
- 싱글톤
- Repository
- javascript
- vuex
- BEAN
- JPA
- Today
- Total
jhhan의 블로그
Spring - JdbcTemplate 본문
이번 포스트에서는 JdbcTemplate에 대해서 다뤄보겠습니다.
JdbcTemplate은 JDBC API에서의 반복 코드를 대부분 제거해 주지만
SQL문은 직접 작성해야 한다는 특징이 있습니다.
JdbcTemplate은 실제로 많이 쓰이기 때문에 알아두면 좋을 것 같습니다.
code를 살펴보겠습니다.
먼저 build.gradle 파일입니다.
위의 그림처럼 설정해줍니다.
아마 implementation 'org.springframework.boot:spring-boot-starter-jdbc' 이 부분을 추가하면 되지 않을까 싶습니다.
프로젝트 구조입니다.
어쩌면 못 보던 것들이 많이 추가되었을 수도 있지만 이번에 추가할 것은 일단
JdbcTemplateMemberRepository 클래스 입니다. -> 이 자바 클래스를 하나 만들어 주세요
public class JdbcTemplateMemberRepository implements MemberRepository {
@Override
public Member save(Member member) {
return null;
}
@Override
public Optional<Member> findById(Long id) {
return null;
}
@Override
public Optional<Member> findByName(String name) {
return null;
}
@Override
public List<Member> findAll() {
return null;
}
}
JdbcTemplateMemberRepository 클래스의 기본 구조입니다.
이제 JdbcTemplate을 사용하겠습니다.
public class JdbcTemplateMemberRepository implements MemberRepository {
private final JdbcTemplate jdbcTemplate;
@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 null;
}
@Override
public Optional<Member> findByName(String name) {
return null;
}
@Override
public List<Member> findAll() {
return null;
}
}
JdbcTemplate은 injection을 받지 않습니다.
대신 DataSource를 Injection 받습니다.
Spring 내에서도 저런 방식으로 사용하는 것을 권장합니다.
※참고 - 생성자가 1개인 경우 @Autowired를 생략해도 됩니다.
위와 같은 경우 @Autowired는 생략이 가능합니다.
생성자가 2개 이상인 경우는 생략하면 안됩니다!!※
다음에 이제 각 메소드들을 작성해보겠습니다.
이 메소드들을 작성하기 전에 RowMapper가 필요합니다.
RowMapper는 결과를 받아오기 위해 매핑하는데 사용됩니다.
public class JdbcTemplateMemberRepository implements MemberRepository {
private final JdbcTemplate jdbcTemplate;
@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 null;
}
@Override
public Optional<Member> findByName(String name) {
return null;
}
@Override
public List<Member> findAll() {
return null;
}
private RowMapper<Member> memberRowMapper() {
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;
}
};
}
}
RowMapper를 작성합니다.
그러면 아마도 RowMapper를 람다 형식으로 변환할 수 있다는 warning이 뜰 것입니다. 바꿔줍니다!
public class JdbcTemplateMemberRepository implements MemberRepository {
private final JdbcTemplate jdbcTemplate;
@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 null;
}
@Override
public Optional<Member> findByName(String name) {
return null;
}
@Override
public List<Member> findAll() {
return null;
}
private RowMapper<Member> memberRowMapper() {
return (rs, rowNum) -> {
Member member = new Member();
member.setId(rs.getLong("id"));
member.setName(rs.getString("name"));
return member;
};
}
}
그럼 람다식이 적용된 형태로 변환됩니다.
그럼 이제 쿼리문 3개만 작성해보겠습니다.
public class JdbcTemplateMemberRepository implements MemberRepository {
private final JdbcTemplate jdbcTemplate;
@Autowired
public JdbcTemplateMemberRepository(DataSource dataSource) {
jdbcTemplate = new JdbcTemplate(dataSource);
}
@Override
public Member save(Member member) {
return null;
}
@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;
};
}
}
다음과 같이 작성을 하면 3개의 메소드를 정상적으로 사용할 수 있습니다.
다음은 save 메소드입니다. save 메소드에서는 새로운 것을 하나 사용해보겠습니다.
@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());
// 여기까지가 insert문으로 보면 됨
Number key = jdbcInsert.executeAndReturnKey(new MapSqlParameterSource(parameters));
member.setId(key.longValue());
return member;
}
SimpleJdbcInsert라는 것을 사용합니다.
Insert문을 작성해서 할 수도 있지만, SimpleJdbcInsert문을 통해서도 Insert를 할 수 있습니다.
- SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate);
-> SimpleJdbcInsert 객체를 하나 생성합니다. - jdbcInsert.withTableName("member").usingGeneratedKeyColumns("id");
-> 테이블 이름을 설정하고, 자동으로 설정된 키값을 지정해줍니다. - 그 다음은 Map을 사용해서 넣어야 할 값들을 차례대로 넣어줍니다.
- Number key = jdbcInsert.executeAndReturnKey(new MapSqlParameterSource(parameters));
-> insert문을 실행하고 키값을 리턴받습니다.
-> MapSqlParameterSource로 Map객체를 받아서 사용합니다.
이런 식으로 하면 나중에 Insert문을 수정할 때 쿼리문을 직접 고치지 않아도 되서 좀 더 수정이 쉬울 것 같습니다.
이런 클래스를 하나 만들었으니 이제 SpringConfig 클래스를 변경해줍니다.
이전에 썼던 MemoryMemberRepository 대신 JdbcTemplateMemberRepository로 변경합니다.
그러면 잘 ~ 작동합니다.
이전 포스트에서 작성한 테스트 코드가 있는데 그걸 활용해보셔도 됩니다.
현재 사용하고 있는 DB는 H2이고, H2를 실행한 다음 테스트를 해보기 바랍니다.
MemberServiceIntegrationTest를 실행해보시면 됩니다.
그러면
그림과 같이 테스트 실행을 성공적으로 마쳤다는 것을 알 수 있습니다.
코드가 잘 짜졌네요 ㅎㅎ
여기까지 해서 JdbcTemplate에 대해 마치겠습니다.
'Spring' 카테고리의 다른 글
스프링 코어(1) - 스프링 컨테이너 & 빈 (1) | 2021.01.04 |
---|---|
Spring - AOP (0) | 2020.10.22 |
Spring - 웹 기능(MVC) (0) | 2020.09.28 |
Spring - DI(2) (0) | 2020.09.26 |
Spring - DI(의존성 주입) (0) | 2020.09.20 |