ABOUT ME

Paragon의 Cave 블로그~

Today
Yesterday
Total
  • [Spring] JPA 끝내기 - JPA
    Spring JPA총정리 2025. 5. 2. 13:06

    minecraft-font

    #JPA

    💡 Spring 정리: JPA 

    📚 JPA란 무엇인가

    • 자바 객체(클래스)를 관계형 데이터베이스 테이블에 자동으로 매핑해주는 표준 API 이고, SQL없이 객체 지향적으로 데이터를 다를 수 있게 도와주는 API이다.

    • JPA는 명세(Interface)에 가깝고 실제로 동작하게 해주는 것은 EclipseLink, DataNucleus, Hibernate(가장 많이 쓰임)
      있다.

    • 주요 기능
    기능 설명
    CRUD 자동화 엔티티 객체 저장, 수정, 삭제, 조회
    엔티티 매핑 클래스 ↔ 테이블, 필드 ↔ 컬럼 자동 매핑
    연관관계 관리 객체 참조 기반으로 조인, 관계 설정 가능
    JPQL 지원 객체 기준 쿼리 사용 (SQL 아님!)
    트랜잭션, 캐시 처리 성능 향상과 일관성 유지 도와줌

     

       배경

    •     JDBC에서는 테이블을 SQL 문으로 직접 생성하고 조작하기 위해 SQL문을 수동으로 작성해야 했다. 
    •     프로젝트의 규모가 커지거나, 다수의 데이터 베이스를 사용하게 될 수록 다은과 같은 문제를 일으킨다.
        1. 테이블이 늘어날수록 SQL 문 관리가 복작하다. 

         2. 기존 SQL의 변경도 전체 코드에 영향을 미친다.

         3. 객체가 가져야 할 역활과 책인이 흐려지게 되어 , 객체지향 원칙이 무너지게 된다

    이를 해결하기 위해 ORM(Object Relation Mapping)이 등장했고 이를 표준화한 API가 JPA(Java Persistence API)가 정의 되었다.

     

    JDBC에서의 테이블 생성하기.(SQL문으로 작성)

     CREATE TABLE member (
             id BIGINT AUTO_INCREMENT PRIMARY KEY,
             name VARCHAR(100) NOT NULL,
            age INT
     );

     

     

    JPA에서 객체

    @Entity                 
    @Table(name = "members") //생략 가능
    class Member{
     @Id 
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;
     private String name;
     private int age;
    
    }

    => JDBC와 다르게 같은 Member 테이블 생성 좀더 직관적으로 변경되었다.

     

    @Entity :  해당 클래스가 JPA에서 관리 되는 엔티티라고 명시함. (필수)

    @Table :  생략이 가능하면서 클래스명(member)이 테이블명으로 그대로 매핑한다.

                     DB 테이블 이름이 클래스명과 다르다면 꼭 @Table 사용

                     팀 작업이나 유지보수 할때 명시적으로 써두는 것이 좋다.

     

     

    Spring 환경에서의 JPA 사용

    이전 JDBC 코드를 JPA로 바뀔때의 코드이다.

     

        JPA 의존성 주입             

    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

     

        JPA의 Repository

    public interface MemberRepository extends JpaRepository<Member, Long> {
    
    	Optional<Member> findById(Long Id);
    
    }
    •        추후에 JPQL 넘어가게되면 다양한 쿼리 메서드가 나오겠지만, 기본 테스트용으로 커버가 가능하다.

    •        JpaRepository는 제네릭으로 관리할 Entity 클래스와 ID 타입을 지정하여 사용한다.

     

         JPA 의 Service

    @Service
    @RequiredArgsConstructor
    public class MemberServiceImpl implements MemberService {
    
        private final MemberRepository memberRepository;
    	@Transactional
    	@Override
    	public MemberResponseDto setMember(MemberRequestDto dto) {
    		Member saveMember = new Member(dto);
    		memberRepository.save(saveMember);
    		return new MemberResponseDto(saveMember);
    	}
    
    	@Transactional(readOnly = true)
    	@Override
    	public MemberResponseDto getMember(Long id) {
    		Member getMember = memberRepository.findById(id)
    			.orElseThrow(()-> new RuntimeException("데이터가 조재하지 않습니다."));
    		return new MemberResponseDto(getMember);
    	}
    
    	@Transactional(readOnly = true)
    	@Override
    	public List<MemberResponseDto> getAllMembers() {
    		List<Member> members = memberRepository.findAll();
    		return members
    			.stream()
    			.map(MemberResponseDto::new)
    			.collect(Collectors.toList());
    	}
    
    	@Transactional
    	@Override
    	public MemberResponseDto updateAllMember(Long id, MemberRequestDto dto) {
    		Member getMember = memberRepository.findById(id)
    			.orElseThrow(()-> new RuntimeException("데이터가 존재하지 않습니다."));
    		getMember.updateMember(dto);
    		return new MemberResponseDto(getMember);
    	}
    
    	@Transactional
    	@Override
    	public MemberResponseDto updateMemberName(Long id, MemberRequestDto dto) {
    		Member getMember = memberRepository.findById(id)
    			.orElseThrow(()-> new RuntimeException("데이터가 조재하지 않습니다."));
    		getMember.updateMember(dto);
    
    		return new MemberResponseDto(getMember);
    	}
    
    	@Transactional
    	@Override
    	public void withdrawMember(Long id) {
    		Member getMember = memberRepository.findById(id)
    			.orElseThrow(()-> new RuntimeException("데이터가 조재하지 않습니다."));
    		 memberRepository.delete(getMember); //존재 체크 (명확하게 있을때만 동작할것)
    
    		memberRepository.deleteById(id);  // 배치 처리나 내부 로직 처럼 신경 안 써도 되는경우 (예외 처리 없음)
    	}
    }

     

            변경점 

    •    오타 수정
    •   @Transaction  추가 //CRUD에 기본 사용 한다. Read(Get)작업할때도 붙여주되 readonly를 true로 해준다.

      https://note8770.tistory.com/123

     

    [Spring] 트랜잭션

    #Transaction💡 Spring 정리: Transaction📘 개념 정리🔥 Transaction트랜잭션(Transaction)은 데이터 베이스 작업의 최소 단위로 여러 개의 작업을 한 번에 처리하거나, 전부 되돌리는 단위이다. ex) 은행 이체

    note8770.tistory.com

    ⚠️ 실수 및 주의사항

      @Transactional 없이 update/delete 수행시 반영 되지 않음.      

    • JPA에서 변경 사항은 트랜잭션이 커밋될 때 DB에 반영된다.
    • @Transactional 없이 값을 수정하면 UPDATE/DELETE가 실행되지 않는다.

         ✅ 그래서 Service 계층의 CUD 메서드에는 항상 @Transactional이 필요하다. 

    @Transactional
    public void deleteMember(Long id) {
        Member member = memberRepository.findById(id).orElseThrow();
        memberRepository.delete(member);
    }

    ✨ 팁 & 인사이트

    JPA의 CRUD (생성, 읽기, 업데이트, 삭제) 작업

    1. 저장 및 업데이트 (Save and Update)
      • save(S entity):
        주어진 엔티티를 저장합니다.
        만약 주어진 엔티티가 새로운 경우에는 생성하고, 이미 존재하는 경우에는 업데이트합니다.
    2. 조회 (Read)
      • findById(ID id) 
         주어진 ID에 해당하는 엔티티를 찾습니다. 결과는 **Optional**로 반환됩니다.
      • findAll()
         모든 엔티티를 조회합니다.

      • findAll(Sort sort)
         정렬 기준에 따라 모든 엔티티를 조회합니다.

      • findAllById(Iterable<ID> ids)
         주어진 ID 목록에 해당하는 엔티티들을 조회합니다.
    3. 삭제 (Delete)
      • delete(T entity)
        주어진 엔티티를 삭제합니다.
      • deleteAll(Iterable<? extends T> entities)
         주어진 엔티티 컬렉션을 삭제합니다.
      • deleteById(ID id)
        주어진 ID를 가진 엔티티를 삭제합니다.

      • deleteAll()
         모든 엔티티를 삭제합니다.
    4. 카운트 및 존재 여부 확인
      • count()
         엔티티의 전체 개수를 반환합니다.

      • existsById(ID id)
        주어진 ID를 가진 엔티티의 존재 여부를 확인합니다.
     

     

Designed by Tistory.