

계층형 구조 사용
- controller, web: 웹 계층
- service: 비즈니스 로직, 트랜잭션 처리
- repository: JPA를 직접 사용하는 계층, 엔티티 매니저 사용
- domain: 엔티티가 모여 있는 계층, 모든 계층에서 사용
**개발 순서: 서비스, 리포지토리 계층을 개발하고, 테스트 케이스를 작성해서 검증, 마지막에 웹 계층 적용**
회원 리포지토리
package com.example.jpaspring.test1.repository;
import com.example.jpaspring.entity.Member;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public class MemberRepository {
@PersistenceContext
private EntityManager em;
public void save(Member member){
em.persist(member);
}
public Member findOne(Long id){
return em.find(Member.class, id);
}
public List<Member> findAll(){
return em.createQuery("select m from Member m", Member.class).getResultList();
}
public List<Member> findByName(String name){
return em.createQuery("select m from Member m where m.name = :name", Member.class)
.setParameter("name", name)
.getResultList();
}
}
**기술 설명**
`@Repository` : 스프링 빈으로 등록, JPA 예외를 스프링 기반 예외로 예외 변환
`@PersistenceContext` : 엔티티 메니저( `EntityManager` ) 주입
`@PersistenceUnit` : 엔티티 메니터 팩토리( `EntityManagerFactory` ) 주입
회원 서비스 코드
package com.example.jpaspring.test1.service;
import com.example.jpaspring.entity.Member;
import com.example.jpaspring.test1.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class MemberService {
private final MemberRepository memberRepository;
/**
* 회원가입 */
@Transactional
public Long join(Member member){
validateDuplicateMember(member);
memberRepository.save(member);
return member.getId();
}
private void validateDuplicateMember(Member member) {
List<Member> findMembers = memberRepository.findByName(member.getName());
if(!findMembers.isEmpty()){
throw new IllegalArgumentException("이미 존재하는 회원입니다");
}
}
/**
* 전체 회원 조회 */
public List<Member> findMembers(){
return memberRepository.findAll();
}
public Member findOne(Long memberId){
return memberRepository.findOne(memberId);
}
}
@Transactional
- 트랜잭션 관리: 메서드나 클래스에 트랜잭션을 적용하여 일관성과 무결성을 보장.
- 영속성 컨텍스트: 영속성 컨텍스트와 상호작용하여 데이터베이스 작업을 관리.
readOnly=true
- 목적: 데이터 변경이 없는 읽기 전용 메서드에 사용.
- 장점:
- 영속성 컨텍스트를 플러시 하지 않아 약간의 성능 향상.
- 데이터베이스 드라이버가 지원하는 경우, DB 성능 향상.
@RequiredArgsConstructor를 사용한 생성자 주입
Lombok 라이브러리는 자바 클래스에서 반복되는 코드를 줄이는 데 도움을 줍니다. @RequiredArgsConstructor 어노테이션은 final 필드나 @NonNull 필드에 대해 생성자를 자동으로 생성해주는 Lombok의 어노테이션입니다. 이를 사용하면 생성자 주입을 더욱 간편하게 구현할 수 있습니다.
package com.example.jpaspring.test1.service;
import com.example.jpaspring.entity.Member;
import com.example.jpaspring.test1.repository.MemberRepository;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
@Transactional
class MemberServiceTest {
@Autowired
private MemberService memberService;
@Autowired
private MemberRepository memberRepository;
@Test
public void 회원가입() throws Exception {
// geive
Member member = new Member();
member.setName("kim");
//when
Long saveId = memberService.join(member);
//Then
Assertions.assertThat(member).isEqualTo(memberRepository.findOne(saveId));
}
@Test
public void 중복_회원_예외() throws Exception {
// Given
Member member1 = new Member();
member1.setName("kim");
Member member2 = new Member();
member2.setName("kim");
// When
memberService.join(member1);
try {
memberService.join(member2);
fail("예외가 발생해야 한다.");
} catch (IllegalStateException e) {
// Then
Assertions.assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
}
}
}
스프링 부트는 datasource 설정이 없으면, 기본적을 메모리 DB를 사용하고, driver-class도 현재 등록된 라이브러를 보고 찾아준다. 추가로 `ddl-auto` 도 `create-drop` 모드로 동작한다. 따라서 데이터소스나, JPA 관련된 별도의 추가 설정을 하지 않아도 된다.
'JPA' 카테고리의 다른 글
| [JPA] JPA 활용1 - 도메인 분석 설계 (0) | 2024.06.30 |
|---|---|
| [JPA] JPA 활용1 - 프로젝트 환경설정 (0) | 2024.06.30 |
| [JPA] JPA 기본값 타입 (0) | 2024.06.30 |
| [JPA] JPA 프록시와 연관관계 관리 (0) | 2024.06.30 |
| [JPA] JPA 고급 매핑 (0) | 2024.06.29 |