`실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발` 강의를 듣고 정리한 자료입니다
✨ 회원 리포지토리 개발
@PersistanceContext
- JPA가 제공하는 표준 애노테이션
- EntityManager 위에 @PersistanceContext 애노테이션을 달게 되면 스프링이 EntityManager 생성 및 주입을 자동으로 해준다
- `@Autowired` 대신 `@PersistanceContext`를 써야 injection 가능
// 엔티티 매니저 팩토리 직접 주입
@PersistenceUnit
private EntityManagerFactory emf;
findAll()
public List<Member> findAll() {
return em.createQuery("select m from Member m", Member.class)
.getResultList();
}
- createQuery로 JPQL을 날리게 된다
- JPQL과 SQL이 기능적으로는 거의 동일하지만 SQL은 테이블 대상, JPQL은 엔티티 객체를 대상으로 쿼리를 날린다
-> from의 대상이 엔티티
✨ 영속성 컨텍스트
- 엔티티를 영구 저장하는 환경
- 엔티티 매니저를 통해 엔티티를 저장·조회 시 엔티티 매니저가 영속성 컨텍스트에 엔티티를 보관하고 관리하게 된다.
em.persist(member);
- 엔티티 매니저 (em)를 사용해 회원 엔티티를 영속성 컨텍스트에 저장한다
특징
1. 엔티티 매니저 생성 시 영속성 컨텍스트도 하나 함께 생성
2. 엔티티 매니저를 통해 영속성 컨텍스트 접근 및 관리 가능
- 영속성 컨텍스트는 key & value를 가지고 있다
-> 강의에서는 key 값이 id 값
✨ 회원 서비스 개발
- `@Service` 애노테이션을 달면 컴포넌트 스캔의 대상이 된다
- 부하를 줄여주므로 읽기에는 가급적 readOnly를 true로 설정해줄 것
@Transactional(readOnly = true)
1. 기본
@Transactional
public class MemberService {
public Long join(Member member) { }
public List<Member> findMembers() { }
public Member findOne(Long memberId) { }
}
2. 트랜잭션 readOnly 활용
public class MemberService {
@Transactional
public Long join(Member member) { }
@Transactional(readOnly = true)
public List<Member> findMembers() { }
@Transactional(readOnly = true)
public Member findOne(Long memberId) { }
}
3. 코드 정리
@Transactional(readOnly = true)
public class MemberService {
@Transactional // 우선권을 가짐
public Long join(Member member) { }
public List<Member> findMembers() { }
public Member findOne(Long memberId) { }
}
-> 그때그때 맞춰서 트랜잭션 달 것
injection
@Autowired
private MemberRepository memberRepository;
- 이렇게 쓸 경우에 변경이 불가하므로 밑과 같은 식으로 setter injection을 사용한다
// setter injection
private MemberRepository memberRepository;
@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
- 테스트 코드 작성 시 메소드를 통해 직접 주입이 가능하다
- 누군가 바꿀 일이 없으므로 자리 차지를 한다 (??)
- 요즘 추세는 `생성자 injection`
private final MemberRepository memberRepository; // 바뀔 일이 없기 때문에
@Autowired // 없어도 됨
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
- 중간에 MemberRepository를 변경할 수 없다 -> setter injection 단점 극복
- 테스트 케이스 작성 시 직접 주입을 해줘야 한다
-> 오류 메시지가 떠 놓치지 않고 무엇을 의존하는지 명확하게 알 수 있다
- final 키워드를 넣으면 컴파일 시 자동으로 체크되기 때문에 좋다
롬복을 활용한 의존 관계 확장
- `@AllArgsConstructor` : 모든 필드를 가지고 생성자를 만들어 줌
- `@RequiredArgsConstructor` : `final`이 있는 필드만을 가지고 생성자를 만들어 줌
MemberRepository injection 변경
@PersistenceContext
private EntityManager em;
✨ 회원 기능 테스트
회원가입 테스트
- 스프링 부트에 올려 테스트하기 위해 사용하는 애노테이션들
@RunWith(SpringRunner.class) // junit과 스프링을 엮어서 실행
// 인데 위에 @RunWith는 Junit4이고, Junit5에서는 문법이 바뀌었다.
@ExtendWith(SpringExtension.class) // ==@RunWith~
@SpringBootTest // 스프링 컨테이너 안에서 테스트를 돌린다
@Transactional // 롤백
public class 클래스명 { }
- persist 한다고 해서 insert문이 나가지는 않는다
- `@Transactional`이 테스트 케이스에 있으면 커밋 대신 롤백을 하므로 insert문을 보고 싶을 때는 @Rollback(false)로 설정할 것
- em.flush(); 로도 insert문 확인 가능
- `@Rollback(false)` 로 하면 db에 반영되는 것이 눈으로 보인다
중복 회원 예외 테스트
- validateDuplicateMember (중복 회원 검증) 이 잘 동작하는지 검증
fail("예외가 발생해야 한다.");
- `fail()` : 예외를 발생시킨다
-> java.lang.AssertionError: 예외가 발생해야 한다
1. try-catch문 활용
@Test
public class 중복_회원_예외() throws Exception {
try {
memberService.join(member2); // 예외가 발생해야 함 (같은 이름을 넣었으므로)
} catch (IllegalStateException e) {
return;
}
}
2. 애노테이션 활용
@Test(expected = IllegalStateException.class)
public class 중복_회원_예외() throws Exception {
memberService.join(member2); // 예외가 발생해야 함 (같은 이름을 넣었으므로)
}
- IllegalStateException 예외를 잡아준다
Memory DB
- DB를 켜지 않아도 테스트를 돌릴 수 있다
1. test 디렉토리 밑 `resources` 디렉토리 생성
2. application.yml 파일 복제
-> main\resources\application.yml 보다 test\resources\application.yml이 더 우선시된다
3. spring: datasource: url을 Memory DB로 교체
✨ 질문 (답은 추후에 추가할 것...)
- Repository 정확히 하는 일이 뭐지?
- `@Repository`를 달면 컴포넌트 빈으로 자동 등록?
- throw와 throws 차이
- 예외 처리 시 의존 관계 발생 -> ?
- `@Transactional` 사용? => 데이터 변경 및 조회?
- 의존성 주입...?
- setter injection 단점
- JPA, Spring -> 커밋, 롤백
- flush()
✨ 참고 자료
'Spring > JPA' 카테고리의 다른 글
[스프링] JPA_S7 : 쿼리 파라미터와 변경 감지 & 병합 (3) | 2024.01.09 |
---|---|
[스프링] JPA_S6 : 주문 도메인 개발 (테스트 코드, 리다이렉트, Valid) (1) | 2024.01.08 |
[Spring] 스프링 패턴, JPA 기본 개념과 스프링 패키지 정리 (1) | 2023.11.23 |
[스프링] JPA_S5 : 상품 도메인 개발 (0) | 2023.11.14 |
[스프링] JPA_S2 : 도메인 분석 설계 (1) | 2023.11.06 |