# 스프링 JdbcTemplate
순수 Jdbc와 동일한 환경설정을 하면 된다. 스프링 JdbcTemplate과 MyBatis 같은 라이브러리는 JDBC API에서 본 반복 코드를 대부분 제거해주만 SQL은 직접 작성해야 한다.
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) {
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 List<Member> findAll() {
return jdbcTemplate.query("select * from member", memberRowMapper());
}
@Override
public Optional<Member> findByName(String name) {
List<Member> result = jdbcTemplate.query("select * from member where
name = ?", memberRowMapper(), name);
return result.stream().findAny();
}
private RowMapper<Member> memberRowMapper() {
return (rs, rowNum) -> {
Member member = new Member();
member.setId(rs.getLong("id"));
member.setName(rs.getString("name"));
return member;
};
}
}
JdbcTemplate을 사용하도록 스프링 설정 변경
package hello.hellospring;
import hello.hellospring.repository.JdbcMemberRepository;
import hello.hellospring.repository.JdbcTemplateMemberRepository;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import hello.hellospring.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class SpringConfig {
private final DataSource dataSource;
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);
return new JdbcTemplateMemberRepository(dataSource);
}
}
# JPA
JPA는 기존의 반복 코드는 물론, 기본적인 SQL도 JPA가 직접 만들어서 실행해준다. JPA를 사용하면 SQL과 데이터 중심의 설계에서 객체 중심의 설계로 패러다임을 전환을 할 수 있어 개발 생산성을 크게 높일 수 있다.
의존성 코드를 작성할 spring-boot-starter-data-jpa 는 내부에 jdbc 관련 라이브러리를 포함한다. 따라서 jdbc는 제거해도 된다. 스프링부트 2.4부터는 JPA 설정인 spring.datasource.username=sa 를 꼭 추가해주어야 한다. 그렇지 않으면 오류가 발생한다.
resources/application.properties 에
spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.jpa.show-sql=true // JPA가 생성하는 SQL을 출력
spring.jpa.hibernate.ddl-auto=none
// JPA는 테이블을 자동으로 생성하는 기능을 제공하는데 none 를 사용하면 해당 기능을 끈다.
JPA 엔티티 매핑 코드까지 작성한 후
회원 리포지토리 코드를 완성한다
package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import javax.persistence.EntityManager;
import java.util.List;
import java.util.Optional;
public class JpaMemberRepository implements MemberRepository {
private final EntityManager em;
public JpaMemberRepository(EntityManager em) {
this.em = em;
}
public Member save(Member member) {
em.persist(member);
return member;
}
public Optional<Member> findById(Long id) {
Member member = em.find(Member.class, id);
return Optional.ofNullable(member);
}
public List<Member> findAll() {
return em.createQuery("select m from Member m", Member.class)
.getResultList();
}
public Optional<Member> findByName(String name) {
List<Member> result = em.createQuery("select m from Member m where
m.name = :name", Member.class)
.setParameter("name", name)
.getResultList();
return result.stream().findAny();
}
}
서비스 계층에는 트랜젝션을 추가한다
import org.springframework.transaction.annotation.Transactional
@Transactional
public class MemberService {}
스프링은 해당 클래스의 메서드를 실행할 때 트랜잭션을 시작하고, 메서드가 정상 종료되면 트랜잭션을 커밋한다. 만약 런타임 예외가 발생하면 롤백한다. JPA를 통한 모든 데이터 변경은 트랜잭션 안에서 실행해야 한다.
JPA를 사용하도록 스프링 설정 변경한 코드는 다음과 같다
package hello.hellospring;
import hello.hellospring.repository.*;
import hello.hellospring.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.persistence.EntityManager;
import javax.sql.DataSource;
@Configuration
public class SpringConfig {
private final DataSource dataSource;
private final EntityManager em;
public SpringConfig(DataSource dataSource, EntityManager em) {
this.dataSource = dataSource;
this.em = em;
}
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
// return new MemoryMemberRepository();
// return new JdbcMemberRepository(dataSource);
// return new JdbcTemplateMemberRepository(dataSource);
return new JpaMemberRepository(em);
}
}
# 스프링 데이터 JPA
스프링 부트와 JPA만 사용해도 개발 생산성이 정말 많이 증가하고, 개발할 코드도 줄어들지만 스프링 데이터 JPA를 사용하면 리포지토리에 구현 클래스 없이 인터페이스만으로 개발을 완료할 수 있다. 반복 개발해온 기본 CRUD 기능도 스프링 데이터 JPA가 모두 제공하여 단순하고 반복이라 생각했던 개발 코드들이 확연하게 줄어든다. 그래서 개발자는 핵심 비즈니스 로직을 개발하는데 집중할 수 있다는 장점이 있고, 실무에서 관계형 데이터베이스를 사용한다면 스프링 데이터 JPA는 필수적이다. 스프링 데이터 JPA는 JPA를 편리하게 사용하도록 도와주는 기술로 JPA 학습이 우선적이다.
인터페이스를 통한 기본적인 CRUD를 제공한다. findByName() , findByEmail() 처럼 메서드 이름만으로 조회할 수 있는 기능, 페이징 기능을 자동 제공한다.
# AOP
모든 메소드의 호출 시간을 측정하려면 전부 처리해야하는데 회원가입과 회원 조회에 시간을 측정하는 기능은 불필요하므로 핵심 관심 사항에 속하지 않는다. 시간을 측정하는 로직은 공통 관심 사항에 속하고, 핵심 비즈니스 사항을 핵심 관심 사항이라고 칭한다.
시간을 측정하는 로직이 핵심 비즈니스 로직과 섞이면 유지보수가 어렵고 시간을 측정하는 로직을 변경할 때 모든 로직을 찾아서 변경해야하는 번거로움이 생기므로 AOP를 사용하면 된다.
'[ Development ] > [ Back-end ] Spring 기본' 카테고리의 다른 글
[Spring 스프링 기본] 객체 지향 원리 적용 (0) | 2023.11.14 |
---|---|
[Spring 스프링 기본] 2. 핵심 원리 이해 1 - 예제 만들기 (0) | 2023.11.07 |
[Spring 스프링 기본] 1. 객체지향설계와 스프링 (0) | 2023.10.22 |
[Spring 스프링 입문] 6. 스프링 DB 접근 기술 (1) (1) | 2023.10.10 |
[Spring 스프링] 5. 회원 관리 예제 - 웹 MVC 개발 (0) | 2023.10.10 |
[Spring 스프링] 4. 스프링 빈과 의존관계 (0) | 2023.10.10 |
[Spring 스프링] 3. 회원 관리 예제 4 - 회원 서비스 개발 및 테스트 (1) | 2023.10.03 |