(인프런) 김영한님의 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술을 공부하고 리뷰한 글입니다.
스프링 DB 접근 기술 4가지 방식대로 차례로 진행할 예정이다.
3. JPA
1. JPA 장점
1. 기존의 반복 코드는 물론이고, 기본적인 SQL도 JPA가 직접 만들어서 실행해준다.
2. SQL과 데이터 중심의 설계에서 객체 중심의 설계로 패러다임을 전환을 할 수 있다.
3. 개발 생산성을 크게 높일 수 있다.
2. JPA를 이용한 코드 작성
1. build.gradle 파일에 JPA, h2 데이터베이스 관련 라이브러리 추가
jdbc를 사용하지 않으므로 아래와 같이 주석 처리하거나 삭제하고 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'
}
2. 스프링 부트에 JPA 설정 추가
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
spring.jpa.hibernate.ddl-auto=create
- spring.jpa.show-sql=true : JPA가 생성한 SQL을 출력
- spring.jpa.hibernate.ddl-auto=create : JPA가 엔티티 정보를 바탕으로 테이블을 자동으로 생성(none으로 설정시 해당 기능 꺼짐)
Q. none으로 설정시 오류가 나는 이유는?..
3. JPA 엔티티 매핑
1) domain/Member 로 이동 Member 클래스 수정
package hello.hellospring.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
// JPA가 관리하는 엔티티
@Entity
public class Member {
// id를 PK로 설정, 자동 생성
@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Long id; // 단순히 데이터 구분을 위해 사용(실제 회원 id가 아님)
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;
}
}
- @Entity: JPA가 관리하는 엔티티
- @Id: 기본키(PK) 매핑
- @GeneratedValue(): 기본 키 자동 생성
4. 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;
}
@Override
public Member save(Member member) {
em.persist(member); // member를 저장
return member;
}
@Override
public Optional<Member> findById(Long id) {
// DB에서 pk가 id인 member 조회(식별자로 조회)
Member member = em.find(Member.class, id);
return Optional.ofNullable(member); // member 객체를 Optional로 반환
}
@Override
public Optional<Member> findByName(String name) {
// 엔티티 객체 member의 name이 name인 member 조회
List<Member> result = em.createQuery("select m from Member m where m.name = :name", Member.class)
.setParameter("name", name)
.getResultList();
return result.stream().findAny(); // member 리스트 객체를 Optional로 반환
}
@Override
public List<Member> findAll() {
// 엔티티 객체 member의 모든 member 조회
return em.createQuery("select m from Member m", Member.class)
.getResultList();
}
}
- EntityManger: 엔티티를 관리하는 객체
- EntityManger.persist(entity): 엔티티 저장
- EntityManger.find(entityClass, primaryKey): 식별자로 조회
- EntityManger.createQuery(JPQL, resultClass): JPQL 쿼리 결과 반환
5. 서비스 계층에 트랜잭션 추가
스프링은 해당 클래스의 메서드를 실행할 때 트랜잭션을 시작하고 메서드가 정상 종료되면 트랜잭션을 커밋하고 만약 런타임 예외가 발생하면 롤백한다.
JPA를 통한 모든 데이터 변경은 트랜잭션 안에서 실행해야 한다.
MemberService 내 join()에서 데이터 삽입을 하는 DB 변경이 일어난다. 그러므로 MemberService 클래스나 join()메서드에 @Transactional을 붙여주어야 한다.
import org.springframework.transaction.annotation.Transactional;
@Transactional
public class MemberService {
...
}
- @Transactional: 클래스나 메서드에 붙여줄 경우, 해당 범위 내 메서드가 트랜잭션이 되도록 보장
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 의존
}
}
Q. dataSource 의존 설정을 따로 하지 않는 이유는?
스프링 부트가 자동으로 다 해주기때문이다.
'Spring > 스프링 입문' 카테고리의 다른 글
[스프링 입문] 07. AOP - AOP가 필요한 상황 (0) | 2022.05.02 |
---|---|
[스프링 입문] 06. 스프링 DB 접근 기술 - 스프링 데이터 JPA (0) | 2022.05.02 |
[스프링 입문] 06. 스프링 DB 접근 기술 - 스프링 JdbcTemplate (0) | 2022.04.29 |
[스프링 입문] 06. 스프링 DB 접근 기술 - 스프링 통합 테스트 (0) | 2022.04.29 |
[스프링 입문] 06. 스프링 DB 접근 기술 - 순수 JDBC (0) | 2022.04.29 |