본문 바로가기

JPA

[JPA] JPA 활용1 - 웹 계층 개발 - 회원 도메인 개발

 

 

 

계층형 구조 사용

  • 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