Notice
Recent Posts
Recent Comments
Link
«   2026/04   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30
Archives
Today
Total
관리 메뉴

memo6759 님의 블로그

2025-11-21(JPA 연관관계, Spring Data JPA) 본문

HDC 학습일지

2025-11-21(JPA 연관관계, Spring Data JPA)

heewon09 2025. 11. 23. 12:16
5. 연관관계
    - 연관 관계의 종류
    - 방향(단방향, 양방향)
    - 주테이블이 무엇인지 고민
    - 연관관계에서 주인이 되는 테이블(주테이블) 외래키를 가지고 있어야한다.
    - 보통 주테이블을 통해 외래키를 관리(등록, 수정, 삭제..)
    - JPA에서는 외래키에 대한 정보가 컬럼값이 아니라 엔티티로 정의해야 한다.
    - 연관관계를 정의하면서 cascade설정을 추가해야한다.
        => 연관관계에 있는 엔티티들에서 주테이블에서 작업을 수행할때 연관된 다른 테이블에서도 관련 작업이 수행되도록 처리
        => CascadeType.ALL : 모든 작업에서
           CascadeType.PERSIST : 엔티티를 저장할때
           CascadeType.REMOVE : 엔티티를 삭제할때

        -@JoinColum 은 DBMS에서 테이블에 만들어지는 외래키의 이름은 사용자가 정의하고 싶은 경우사용
        => JPA내부에 정의되어 있는 명명규칙대로 생성된다
        => JoinColumn이 정의된 엔티티가 주테이블의 역할이 된다.
        - 지연로딩과 즉시 로딩을 적절하게 적용
         - 지연로딩은 바로 생성하지 않고 객체가 실제로 사용될때 로딩되는 것을 의미
         - 즉시로딩은 모든 테이블을 한 번에 조회할 수 있도록 바로 로딩하는 것을 의미
         -
    1) 일대일
       - 1:1관계는 양쪽 엔티티가 서로 하나의 관계를 가지므로 왜래키를 어떤 테이블에 두어도 상관이 없다
       - 외래키는 주테이블에 둔다.
       - @One to One 을 이용해서 작업

    2) 일대다(단방향)
        - pk엔티티에서 fk엔티티의 정보들을 가지고 있는 관계
        - pk엔티티에서 fk엔티티 정보를 List에 갖고 있도록 정의
        - pk와 fk관계가 있어야 작업이 가능
        ex) 한 부서에서 근무하는 직원 목록
            주문번호에 주문한 물건들의 명세와 주문에 대한 일반적인 내용
            게시글에 연결된 첨부파일...
            사원 한 명의 자격사항, 경력사항....

         - @OneToMany을 이용해서 작업
         - 일대다관계에서 @joinColumn은 외래키테이블에 외래키로 정의할 컬럼명


    3) 다대일(단방향)

        - JPA에서 가장 중요하고 가장 많이 사용되는 연관관계
        - 다에서 해당하는 엔티티에 외래키를 설정하고 이를 통해서 일에 해당하는 엔티티
        - @ManyToOne
        - fk테이블을 나타내는 엔티티쪽에서 pk정보를 갖고 있는 것
        -
    4) 다대다
        -> 일대다와 다대일로 표현 하는 것이 일반적
6. 양방향
    -카테고리에서 상품을 카테고리를 양방향으로 모두 접근할 수 있도록 만드는 작업
    - 두 객체가 서로의 존재를 인지하고 참조할 수 있도록 정의하는 방식
    상품 => 카테고리(단방향) - 다대일
    카테고리 = > 상품(단방향) - 일대다
    - 양방향은 단방향의 연관관계를 두 개 정의하고 사용
    - Category엔티티에서 Product의 정보를 갖고 있고 Product에서 Category정보를 갖고 있도록 작업
    - 양방향으로 작업하는 경우 기준을 정해서 작업한다. 즉 주테이블을 정하고 작업하기
    - ManyToOne 엔티티가 주 테이블이 된다.
    - 주 테입블이 아닌 곳에서는 mappedBy속성을 이용해서 현재 엔티티가 주테이블의 엔티티가 아니고 주테이블의 컬럼을 참조한다는 것을 명시
                        ----------------------------
                        mappedBy속성에 참조하는 컬럼명을 정확하게 정의


7. Spring data JPA
    - Spring data jpa는 스프링프레임워크에서 JPA를 쉽게 사용할 수 있도록 제공되는 기능
    - 개발자가 반복해서 사용하는 CLRUD의 기능을 비롯한 많은 기능을 구현해서 제공하므로 편하게 작업할 수 있다.
    - 구현 클래스없이 인터체이스만 가지고 작업이 가능
    - 기본 CLRUD작업을 위해서 Spring data JPA가 제공하는 JpaRepository만 상속받아 인페이스만 만들면
      기본 기능을 spring data JPA가 만들어서 추상화해준다.
    - spring boot application 이 start될때 JpaRepository를 상속하는 모든 객체의 구현체를 만들어서 제공한다.
                                                                            ======================\
                                                                            객체를 상속해서 메소드를 오버라이딩


    - 이렇게 만들어지는 구현체를 프록시 객체
    - class jdk.proxy1.$Proxy131이런식으로 객체의 정보가 출력되면 프록시 객체



Spring Data JPA 완전 정리

2. Repository 작성

package com.example.jpatest.springdatajpa;

import com.example.jpatest.mappedBy.EmpEntity4;
import org.springframework.data.jpa.repository.JpaRepository;

public interface EmpRepository extends JpaRepository<EmpEntity4, String> {


}

EmpRepository

DTO (요청/응답)

package com.example.jpatest.springdatajpa;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor

public class EmpRequestDTO {
    private String userId;
    private String name;
    private String addr;
}
package com.example.jpatest.springdatajpa;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class EmpResponseDTO {
    private  String firstName;
    private String lastName;
}

테스트 코드 (EmpRepositoryTest)

package com.example.jpatest.springdatajpa;

import com.example.jpatest.mappedBy.DeptEntity;
import com.example.jpatest.mappedBy.EmpEntity4;
import com.example.jpatest.mappedBy.PrivateInfoEntity4;
import jakarta.transaction.Transactional;
import org.assertj.core.util.Lists;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.test.annotation.Rollback;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
@Transactional
@Rollback(value = false)
class EmpRepositoryTest {
    @Autowired
    DeptRepository deptRepository;
    @Autowired
    EmpRepository empRepository;
    @Test
   public void test1(){
        //스프링이 만들어주는 프록시 객체 확인
        System.out.println("++++++++++++실제실행되는 클래스 확인+++++++++++++++++++++++");
        System.out.println(deptRepository.getClass());
        System.out.println(empRepository.getClass());
        System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++");
        //내림차순정렬
        //Sort.by(정렬기준,정렬할 컬럼명)
        print(empRepository.findAll(Sort.by(Sort.Direction.DESC,"addr")));
        System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++");
        //기본키로 레코드 조회하기
        EmpEntity4 emp = empRepository.findById("bts1").get();
        System.out.println(emp);
        System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++");
        //userId를 여러 개 주고 만족하는 데이터를 조회
//        List<String> idlist = new ArrayList<>();
//        idlist.add("bts1");
//        idlist.add("bts2");
//        idlist.add("bts3");
//        print(empRepository.findAllById(idlist));
        print(empRepository.findAllById(Lists.newArrayList("bts1","bts4","bts5")));
    }

    /*
    * save메소드는 객체를 새럽게 만들어서 작업하는 경우에는 insert문이 만들어져서 실행되고
    * 조회한 객체의 setter메소드를 이용해서 값을 변경하거나
    * 1차 캐시에 있는 개체가 변경되는 경우 update문을 만들어서 작업한다.
    *
    *
    * */
    @Test
    public void findtest(){
        //데이터모두조회하기
        List<EmpEntity4> emplist = empRepository.findAll();
        print(emplist);

    }

    @Test
    public void insert(){
        //레코드 하나 저장하기
        DeptEntity dept = deptRepository.findById(1L).get();
        EmpEntity4 emp = new EmpEntity4("bts77", "뷔", "제주",
                new PrivateInfoEntity4("bts77", "재즈", "귀여움"),dept);
        empRepository.save(emp);

    }
    void print(List<EmpEntity4> emplist){
        for(EmpEntity4 emp:emplist){
            System.out.println(emp);
        }
    }

    @Test
    public void update(){
        //update나 delete할 레코드를 조회
        EmpEntity4 bts1 = empRepository.findById("bts1").get();
        System.out.println("조회한레코드"+bts1);
        bts1.setAddr("강원도");
        empRepository.save(bts1);

    }
    @Test
    public void readtest(){
        //갯수
        long count = empRepository.count();
        System.out.println("레코드갯수=>"+count);
        //레코드 존재유무
        System.out.println("실행결과==>"+empRepository.existsById("bts1"));
        System.out.println("실행결과==>"+empRepository.existsById("bts11111111111111"));

    }
    @Test
    public void insertall(){
        DeptEntity dept =  deptRepository.findById(1L).get();
        EmpEntity4 emp1 = new EmpEntity4("bts88","뷔","제주",
                new PrivateInfoEntity4("bts88","재즈","귀여움"),dept);
        EmpEntity4 emp2 = new EmpEntity4("bts99","뷔","제주",
                new PrivateInfoEntity4("bts99","재즈","귀여움"),dept);
        EmpEntity4 emp3 = new EmpEntity4("bts100","뷔","제주",
                new PrivateInfoEntity4("bts100","재즈","귀여움"),dept);

        empRepository.saveAll(Lists.newArrayList(emp1,emp2,emp3));


    }

    //spring data jpa에서 제공되는 페이징 처리
    @Test
    public void pagetest(){
        //pageRequest객체를 만들어서 repository의 findall호출
        Page<EmpEntity4> pagelist = empRepository.findAll(PageRequest.of(0, 5));
                                                                //==========================
        //                                                  현재페이지 번호  한 페이지에서 보여줄 레코드 갯수
        //                                                      0번 부터시작
        System.out.println("pagelist:"+pagelist);
        System.out.println("total(전체레코드수)"+pagelist.getTotalElements());
        System.out.println("전페페이지"+pagelist.getTotalPages());
        System.out.println("t현제조회한 레코드수"+pagelist.getNumberOfElements());
        System.out.println("정렬"+pagelist.getSort());
        System.out.println("total(전체레코드수)"+pagelist.getSize());
        //실제 데이터 꺼내기

        List<EmpEntity4> content = pagelist.getContent();
        System.out.println("content:"+content);



    }
}

pagetest() — 페이징 처리

 
Page<EmpEntity4> pagelist = empRepository.findAll(PageRequest.of(0, 5));
  • 0번 페이지(첫 페이지)
  • 페이지당 5개 레코드

출력:

pagelist.getTotalElements(); // 전체 레코드 수 pagelist.getTotalPages(); // 전체 페이지 수 pagelist.getNumberOfElements(); pagelist.getSize(); pagelist.getSort(); pagelist.getContent(); // 실제 데이터 리스트

➡ Spring Data JPA는 페이징 기능을 기본 제공한다.


Spring Data JPA 핵심 요약 

 JPARepository 상속만 하면

→ CRUD, 페이징, 정렬 기능을 자동으로 제공

 save()

  • 새 엔티티 → INSERT
  • 영속상태 엔티티 수정 → UPDATE 자동

 findAll(), findById(), existsById(), count()

→ 기본 제공 메소드

 findAll(PageRequest)

→ 페이징 처리 자동화

 findAll(Sort)

→ 정렬 자동화

'HDC 학습일지' 카테고리의 다른 글

2025-11-27(Open CV) - 현업자 특강  (0) 2025.11.27
2025-11-26(컴퓨터 비전)- 현업자 특강  (0) 2025.11.27
2025-11-20(JPA)  (0) 2025.11.20
2025-11-18(Spring)  (0) 2025.11.19
2025-11-17(Spring)  (1) 2025.11.17