@Repository 컴포넌트 스캔 대상이 되어 스프링 빈으로 등록, JPA 예외를 스프링 기반 예외로 예외 변환
@PersistenceContext 스프링이 생성한 엔티티 메니저( EntityManager
) 주입
→ @Autowired와 생성자로 대체 → @Autowired 생략 → @RequiredArgsContructor로 대체
(스프링 데이터 JPA를 사용하면 @Autowired로 EntityManager
도 주입 가능)
@Repository
@RequiredArgsConstructor
public class MemberRepository {
private final EntityManager entityManager;
...
}
@PersistenceUnit 엔티티 메니터 팩토리( EntityManagerFactory
) 주입
save()
public void save(Member member) {
entityManager.persist(member);
}
persist
: 영속성 컨텍스트에 Member 객체를 넣어 트랜잭션이 커밋 되는 시점에 DB에 반영
findOne()
단건 조회 → 첫번째 파라미터엔 타입, 두번째 파라미터엔 PK
findAll()
findByName()
public List<Member> findByName(String name) {
return entityManager.createQuery("select m from Member m where m.name = :name", Member.class)
.setParameter("name", name)
.getResultList();
}
:name
으로 파라미터 바인딩
@Service 컴포넌트 스캔의 대상이 되어 자동으로 스프링 빈으로 등록
@Transactional 트랜잭션, 영속성 컨텍스트
*readOnly=true*
옵션: 조회 성능 최적화@Autowired 생성자 Injection 많이 사용, 생성자가 하나면 @Autowired 생략 가능
@RequiredArgsContructor final
필드(필수 필드)를 주입하는 생성자만 생성해주는 애노테이션
@RequiredArgsConstructor
public class MemberService {
private final MemberRepository memberRepository;
...
}
join()
중복 회원 검증
private void validateDuplicateMember(Member member) {
List<Member> foundMembers = memberRepository.findByName(member.getName());
if (!foundMembers.isEmpty()) {
throw new IllegalStateException("이미 존재하는 회원입니다.");
}
}
실무에서는 검증 로직이 있어도 멀티 쓰레드 상황을 고려해서 회원 테이블의 회원명 컬럼에 유니크 제약 조건을 추가하는 것이 안전하다.
findMembers()
findOne()
@RunWith(SpringRunner.class) 스프링과 테스트 통합
@SpringBootTest 스프링 부트 띄우고 테스트(이게 없으면 @Autowired 다 실패)
@Transactional 반복 가능한 테스트 지원, 각각의 테스트를 실행할 때마다 트랜잭션을 시작하고 테스트가 끝나면 트랜잭션을 강제로 롤백 ****(이 어노테이션이 테스트 케이스에서 사용될 때만 롤백)
@Test
public void join() {
// given
Member member = new Member();
member.setName("jeongwon");
// when
Long savedId = memberService.join(member);
// then
Assertions.assertEquals(member, memberRepository.findOne(savedId));
}
JPA의 같은 트랜잭션 안에서 pk(id)값이 똑같으면(= 같은 엔티티) 같은 영속성 컨텍스트에서 관리가 된다.
@Test(expected = IllegalStateException.class)
public void validateDuplicateMembers() {
// given
Member member1 = new Member();
member1.setName("jeongwon");
Member member2 = new Member();
member2.setName("jeongwon");
// when
memberService.join(member1);
memberService.join(member2);
// then
fail("예외가 발생해야 한다.");
}
fail
해당 위치의 코드가 실행되면 안 될 때 테스트를 실패시키고 종료한다.
테스트는 케이스 격리된 환경에서 실행하고, 끝나면 데이터를 초기화하는 것이 좋다.
→ 그런 면에서 메모리 DB를 사용하는 것이 가장 이상적이다.
추가로 테스트 케이스를 위한 스프링 환경과, 일반적으로 애플리케이션을 실행하는 환경은 다르므로 설정 파일을 다르게 사용한다.
test/resources/application.yml
url: jdbc:h2:mem:test
spring:
logging.level:
org.hibernate.SQL: debug
스프링 부트는 datasource 설정이 없으면, 기본적으로 메모리 DB를 사용하고, driver-class도 현재 등록된 라이브러리를 보고 찾아준다. 추가로 ddl-auto 도 create-drop (애플리케이션 종료 시점에 drop 쿼리를 날려 모두 초기화) 모드로 동작한다. 따라서 데이터소스나, JPA 관련된 별도의 추가 설정을 하지 않아도 된다.