JDBC Template에서는 직접 SQL Query문을 생성해야 하지만, JPA를 사용하면 이를 자동으로 만들어서 개발 생산성을 높여줄 뿐만 아니라 SQL과 데이터 중심의 설계에서 객체 중심의 설계로 패러다임을 전환을 할 수 있다.
사전 설정
# build.gradle 추가
Spring Boot가 자동으로 Entity Manager를 생성해준다.
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
# application.properties 추가
JPA를 사용하면 객체를 보고 알아서 테이블을 만들어버린다. 본 실습에서는 테이블을 만들어놨기 때문에 none으로 설정.
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
JPA는 객체와 관계형 DB 매핑이다. (Object Relational Mapping) 그러므로 JPA를 쓰려면 Entity를 매핑해야 하는데, 이를 @Entity 어노테이션을 통해 생성한다.
@Entity
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
public Long getId() {
return id;
}
...
}
JPA는 인터페이스이기 때문에, '구현체'로 hibernate이 필요하다. 그러므로 external libraries에 hibernate이 추가되어 있는지 확인한다. 꼭 hibernate뿐만 아니라 직접 JPA 구현체를 구현해도 되며, 다른 구현체를 사용해도 괜찮다.
JPA의 핵심
1. 객체와 관계형 DB의 매핑(Object Relational Mapping)
2. Persistence Context(JPA 내부 동작)
"Entity를 영구 저장하는 환경"이라는 의미를 가진다. 실제 DB에 저장하는 것이 아니라, Persistence Context라는 환경에 영속적으로 관리한다는 것이다.
Persistence Context의 이점
① 버퍼링, 캐싱
Persistence Context 내부에는 1차 캐시가 존재한다.
Map<Key, Value> 형식으로 1차 캐시에 저장되는데, Key는 @id로 선언한 필드 값, Value는 해당 Entity 자체이다.
find() 명령은 DB보다 먼저 1차 캐시를 조회하지만, 성능상 큰 이점을 가지지는 않는다.
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
// DB 저장이 X, Persistence Context에 의해 Entity를 영속적으로 관리
entityManager.persist(member);
// 1차 캐시에서 조회
Member findMember = entityManager.find(Member.class, "Member1");
JpaMemberRepository
JPA를 사용하려면 EntityManager를 주입받아야 한다. EntityManager는 Entity를 CRUD하는 등 Entity와 관련된 모든 일을 처리한다. Entity를 저장하는 가상의 저장소라고 생각하자.
public class JpaMemberRepository implements MemberRepository {
private final EntityManager em;
public JpaMemberRepository(EntityManager em) {
this.em = em;
}
...
}
EntityManager의 명령어 통해 여러 기능을 사용할 수 있다.
# persist
처음 Entity 객체를 생성했다면, 이 객체는 Transient/New 상태라고 불린다. 이 상황에서 persist 없이 곧바로 Transaction을 종료한다면 DB 상에는 아무 일도 일어나지 않는다.
이 Entity를 Persistence Context에 관리(저장)하도록 하기 위해서 persist 메서드를 호출해야 한다.
실제로 DB에 저장하는 것이 아니라 Persistence Context를 통해 Entity를 영속화한다는 의미를 가진다.
정확히 말하면 persist() 시점에는 Entity를 Persistence Context에 저장하는 것이다.
# find
Persistence Context에 해당 객체가 있다면 그대로 반환하는 명령어이다.
# createQuery
find만으로는 내가 원하는 검색을 하기에 어려움이 많다. 다양한 조회 조건으로 검색을 해야 원하는 결과를 얻을 수 있는데, 이러한 경우 Entity 객체를 대상으로 검색을 할 수 있도록 하는 방법인 JPQL을 사용하는 것이다. JPQL은 데이터베이스를 조회하는 SQL이 아니라 객체를 조회하는 객체 지향 쿼리이다.
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() {
// select m: 객체 자체를 select
return em.createQuery("select m from Member m", Member.class)
.getResultList();
}
}
SpringConfig
@Configuration
public class SpringConfig {
private EntityManager em;
@Autowired
public SpringConfig(EntityManager em) {
this.em = em;
}
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new JpaMemberRepository(em);
}
}
MemberService
데이터를 저장하거나 변경하기 위해서는 Transaction(@Transactional)이 필요하다. 상위 클래스에 선언해도 되고, 데이터에 접근하는 해당 함수에 선언해도 된다.
출처
https://www.inflearn.com/course/스프링-입문-스프링부트
'🌱 Spring > 회원 관리 예제' 카테고리의 다른 글
[Spring] 스프링 데이터 JPA (0) | 2023.07.08 |
---|---|
[Spring] 통합 테스트 (0) | 2023.07.07 |
[Spring] 웹 UI 및 비즈니스 로직(가입, 조회) 연결 (0) | 2023.07.05 |
[Spring] Spring Bean과 의존 관계 (0) | 2023.07.05 |
[Spring] 회원 서비스 테스트(Dependency Injection) (0) | 2023.07.05 |