[김영한 인프런 스프링 입문] 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
섹션5. 회원 관리 예제 - 웹 MVC 개발
1) H2 데이터베이스 설치
2) 순수 JDBC
3) 스프링 통합 테스트
4) 스프링 JdbcTemplate
5) JPA
6) 스프링 데이터 JPA
1) 기존의 반복 코드를 줄일 뿐 아니라, 기본적인 SQL도 JPA가 직접 만들어서 DB에 접근할 수 있다
2) SQL과 데이터 중심 설계에서 객체 중심의 설계로 패러다임을 전환할 수 있다
3) 개발 생산성을 크게 향상시킬 수 있다
1. build.gradle 파일에 JPA를 사용하기 위한 라이브러리 추가
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
//implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.h2database:h2'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
jdbc를 사용하지 않으므로 아래와 같이 주석 처리하거나 삭제
기존 코드에서 'implementation 'org.springframework.boot:spring-boot-starter-data-jpa''부분 추가
라이브러리를 추가하고 gradle refresh
2. 스프링 부트에 JPA 설정 추가
resources/application.properties 파일에 다음과 같은 코드를 추가하여 스프링 부트가 JPA를 사용하는 것과 관련된 설정 해주기
주의 여기서 띄어쓰기 하면 오류 발생
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
spring.jpa.hibernate.ddl-auto=none
3. JPA 엔티티 매핑
1) domain/Member 로 이동 Member 클래스 수정
package hello.hellospring.domain;
import javax.persistence.*;
@Entity
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
//@Column(name="username")
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
4. JPA 회원 리포지토리
repository 폴더에 다음과 같은 JpaMemberRepository 파일 생성하고 MemberRepository를 구현하여 다음과 같은 코드 작성
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;
}
@Override
public Member save(Member member) {
em.persist(member);
return member;
}
@Override
public Optional<Member> findById(Long id) {
Member member = em.find(Member.class,id);
return Optional.ofNullable(member);
}
@Override
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();
}
@Override
public List<Member> findAll() {
return em.createQuery("select m from Member m",Member.class)
.getResultList();
}
}
5. 서비스 계층에 트랜잭션 추가
스프링은 해당 클래스의 메서드를 실행할 때 트랜잭션을 시작하고 메서드가 정상 종료되면 트랜잭션을 커밋하고 만약 런타임 예외가 발생하면 롤백한다
JPA를 통한 모든 데이터 변경은 트랜잭션 안에서 실행해야 한다.
MemberService 내 join()에서 데이터 삽입을 하는 DB 변경이 일어난다. 그러므로 MemberService 클래스나 join()메서드에 @Transactional을 붙여주어야 한다
import org.springframework.transaction.annotation.Transactional;
@Transactional
public class MemberService {
...
}
6. JPA를 사용하도록 스프링 설정 변경
JpaMemberRepository가 EntityManger를 의존하도록 생성자 주입 하기
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;
import javax.xml.crypto.Data;
@Configuration
public class SpringConfig {
private final EntityManager em;
// SpringConfig가 EntityManger 의존(생성자 주입)
public SpringConfig(EntityManager em) {
this.em = em;
}
// 스프링 빈에 등록
@Bean
public MemberService memberService() {
return new MemberService(memberRepository()); // 의존 관계 설정
}
// 나중에 DB가 정해지면 바뀔 부분
@Bean
public MemberRepository memberRepository() {
//return new MemoryMemberRepository();
//return new JdbcMemberRepository(dataSource); // JdbcMemberRepository가 dataSource 의존
//return new JdbcTemplateMemberRepository(dataSource); // JdbcTempalteRepository가 dataSource 의존
return new JpaMemberRepository(em); // JpaMemberRepository가 EntityManager 의존
}
}
테스트가 정상적으로 잘 동작하는 것을 확인할 수 있다. 로그로 출력되는 SQL문 또한 JPA 리포지토리를 사용할 때의 테스트 결과와 동일하다
이렇게 단순히 인터페이스만을 생성하여 동작이 가능한 것은 인터페이스를 생성할 때 JpaRepository를 extends했기 때문이다. 해당 인터페이스를 내려받으면 스프링 데이터 JPA에서 자동으로 인터페이스의 구현체를 만들고, 스프링 빈으로 자동으로 등록한다

우리가 인터페이스를 생성할 때 내려받은 JpaRepository는 PagingAndSortingRepository인터페이스를 내려받고, 이는 CrudRepository, Repository를 내려받는다. 해당 인터페이스들에는 save(), findOne(), findAll(), findById()과 같이 우리가 이전에 직접 구현했던 기능들 뿐 아니라, 이 외에도 기본적인 CRUD를 수행하거나 단순 조회를 수행하는 기능과 count(), delete()와 같은 기능 또한 구현되어있다.
또 findByName() 이나 findByEmail()과 같이 공통으로 뽑을 수 없는 메서드들은 따로 인터페이스 안에 위와 같이
'Optional<Member> findByName(String name);' 처럼 명시하기만 하면 스프링 데이터 JPA에서 자동으로
'select m from Member m where m.name =?'과 같은 JPQL 쿼리를 생성하여 이를 사용할 수 있다
(예를 들어 findByEmail()이라면 'Optional findByEmail(String email);' 과 같은 코드를 추가하기만 하면 된다)
스프링 데이터 JPA는 위와 같이 인터페이스를 통한 기본적인 CRUD 기능뿐 아니라 findByName()과 같이 메서드 이름만으로 조회를 가능하게 하고, 페이징 기능 또한 제공하며 구현 클래스를 작성하지 않고 인터페이스만을 작성하여 개발이 끝나도록 한다
| [인프런 스프링 입문] #5 회원 관리 예제 - 웹 MVC 개발 (0) | 2023.10.19 |
|---|---|
| [인프런 스프링 입문] #3 회원 관리 예제 - 백엔드 개발 (1) | 2023.10.12 |
| [인프런 스프링 입문] #2 스프링 웹 개발 기초 (0) | 2023.10.05 |
| [인프런 스프링 입문] #1 프로젝트 환경설정 (1) | 2023.10.05 |