반응형
JPA는 기존의 반복코드와 SQL문을 직접 만들어준다.
JPA를 사용하면, SQL과 데이터 중심의 설계에서 객체 중심의 설계를 할 수 있다.
build.gradle 디펜던시 수정 - jdbc 주석 처리 and jpa 추가
// implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
application.properties 수정
# jpa 작성된 쿼리를 확인 할 수 있도록 설정
spring.jpa.show-sql=true
# 자동 테이블 생성기능 끄기
spring.jpa.hibernate.ddl-auto=none
jpa는 인터페이스이고 hibernate는 구현체라고 생각하면 된다.
Member 클래스를 JPA와 연동 되도록 코드 수정
package hello.hellospring.domain;
import javax.persistence.*;
// @Entity: jpa가 관리하는 객체라고 선언
@Entity
public class Member {
// @Id: PK 맵핑 / @GeneratedValue: ID가 자동으로 생성되도록 설정
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// 테이블의 name 컬럼 맵핑 설정
@Column(name = "name")
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;
}
}
JpaMemberRepository 생성
jpa를 사용하기 위해서는 반드시 EntityManager를 주입받아서 사용하여야 한다.
package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import java.util.List;
import java.util.Optional;
public class JpaMemberRepository implements MemberRepository{
// jpa를 사용할려면 EntityManager를 주입 받아야 한다.
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) {
// em.find(조회 타입, 식별자);
Member member = em.find(Member.class, id);
return Optional.ofNullable(member);
}
@Override
public Optional<Member> findByName(String name) {
// em.createQuery("실행할 쿼리", 반환할 타입)
List<Member> result = em.createQuery("select m from Member m where m.name = :name", Member.class)
// 파라미터값 선언
.setParameter("name", name)
// 리스트 형태로 반환
.getResultList();
// Optional로 처리하기 때문에 아래와 같이 반환 stream().findAny(): 첫번째 찾은 데이터를 반환(순서가 중요하지 않을때 사용)
return result.stream().findAny();
}
@Override
public List<Member> findAll() {
// em.createQuery("실행할 쿼리", 반환할 타입)
List<Member> result = em.createQuery("select m from Member m", Member.class)
.getResultList();
return result;
}
@Override
public void clearStore() {
}
}
jpa의 기능은 항상 트랜잭션기능 안에서 동작되어야 한다.
따라서 각 기능을 구현한 MemberService 클래스에 @Transactional 어노테이션 선언
SpringConfig 클래스에서 jpa기능을 사용하도록 코드 수정
package hello.hellospring.service;
import hello.hellospring.repository.*;
import org.springframework.beans.factory.annotation.Autowired;
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 {
// JDBC 사용을 위해 DataSource을 주입 받는다.
// private DataSource dataSource;
//
// @Autowired
// public SpringConfig(DataSource dataSource){
// this.dataSource = dataSource;
// }
// JPA사용을 위해 EntityManager을 주입 받는다.
private EntityManager em;
@Autowired
public SpringConfig(EntityManager em) {
this.em = em;
}
// 스프링 빈 등록
@Bean
public MemberService memberService() {
// @Autowired와 같이 동작
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
// 구현체를 리턴해줌
// return new MemoryMemberRepository();
// JDBC 연결로 변경경
// return new JdbcMemberRepository(dataSource);
// JDBCTemplate 연결로 변경
// return new JdbcTemplateMemberRepository(dataSource);
return new JpaMemberRepository(em);
}
}
테스트 진행
package hello.hellospring.service;
import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Commit;
import org.springframework.transaction.annotation.Transactional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
// 테스트는 한글로 바꾸어도 된다.
// 빌드될때 테스트코드는 포함되지 않는다.
@SpringBootTest
@Transactional
class MemberServiceIntegrationTest {
// 컨테이너로 부터 주입받는다.
// 테스트를 진행할 때는 편하게 @Autowired 사용하면 된다. -> 필드 주입 방법(DI)
@Autowired MemberService memberService;
@Autowired MemberRepository memberRepository;
@Test
// @Commit: 실제로 데이터가 저장되도록 선언
@Commit
void 회원가입() {
// given
Member member = new Member();
member.setName("spring");
// when
Long saveId = memberService.join(member);
// then
Member findMember = memberService.findOne(saveId).get();
assertThat(member.getName()).isEqualTo(findMember.getName());
}
@Test
public void 중복_회원_예외(){
// given
Member member1 = new Member();
member1.setName("spring");
Member member2 = new Member();
member2.setName("spring");
// when
memberService.join(member1);
// member2를 join 할 때 IllegalStateException이 발생하것을 예상하는 코드
IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));
assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
// assertThrows(NullPointerException.class,() -> memberService.join(member2));
/* try {
memberService.join(member2);
fail();
} catch (IllegalStateException e) {
Assertions.assertThat(e.getMessage()).isEqualTo("이미 존해하는 회원입니다.12333");
}*/
// then
}
}
반응형
'코드로 배우는 스프링 부트 - 인프런' 카테고리의 다른 글
AOP (0) | 2022.08.24 |
---|---|
스프링 데이터 JPA (0) | 2022.08.23 |
스프링 JDBC Template (0) | 2022.08.19 |
스프링 통합 테스트 (0) | 2022.08.18 |
순수 JDBC (0) | 2022.08.17 |