쿠릉쿠릉 쾅쾅
쿠릉쿠릉 쾅쾅
쿠릉쿠릉 쾅쾅
250x250
전체 방문자
오늘
어제
  • 분류 전체보기
    • HTML CSS
    • 잡담
    • 프로그래밍 꿀팁 사이트
    • 코딩 도서
    • [자바]
      • 디자인 패턴
      • 자바의 정석 - 3판
      • 자바
      • 자바 문법
    • git
    • [TDD]
    • 개발 서적 독후감
      • 클린 코더
      • 토비 스프링3
      • 객체지향의 사실과 오해
      • 모던 자바 인 액션
      • 엘레강트 오브젝트
    • CS
      • 운영체제
      • HTTP
    • [SQL]
      • SQL 기초
      • 혼자공부하는SQL
    • [ Spring ]
      • REST API
      • Spring Toy
      • Spring 에러
      • Spring
      • Spring 입문
      • Spring 핵심 원리
      • SpringMVC 1편
      • SpringMVC 2편
      • Spring Boot를 이용한 RESTful We..
      • Batch
    • [JPA]
      • JPA
      • JPA 에러
      • JPA 프로그래밍 - 기본편
      • 스프링 부트와 JPA 활용 1 - 웹 애플리케이..
      • 실전! 스프링 부트와 JPA 활용2 - API 개..
      • 실전! 스프링 데이터 JPA
      • 실전! Querydsl
    • 인텔리제이
    • [DB]
      • DB
      • H2
    • Gradle
    • 면접
    • [알고리즘]
      • 알고리즘
      • 자료구조
      • 자바 알고리즘 공부
    • [프로젝트]
    • 쿠릉식 객체지향 사고
    • 리눅스

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • querydsl
  • GitHub
  • 깃허브
  • 스프링부트
  • Git
  • java
  • 스프링
  • SQL
  • http
  • 백준
  • REST API
  • 재귀
  • JPA
  • 알고리즘
  • springboot
  • Spring
  • 함수형인터페이스
  • MVC
  • 자바
  • 자료구조

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
쿠릉쿠릉 쾅쾅

쿠릉쿠릉 쾅쾅

[JPA]/JPA

[JPA] 프록시의 필드값을 조회 때는 get 방식으로 조회하자!

2022. 6. 7. 17:00
728x90

 

어느 엔티티 안에 속해있는 프록시 객체의 멤버변수 값을 가져오고 싶을 때 어떻게 해야할까에 대한 문제가 생겨서 이 글을 작성하게 됐다.

예를 들어, Member 엔티티 안에 Team 엔티티를 지연 로딩으로 설정한다. 그리고 Member 엔티티에서 Team 엔티티의 이름을 가져올 때  1안과 2안 중 어느 것이 맞을지에 대해 논의하여 작성하게 됐다.

@Entity
@Getter
@NoArgsConstructor
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    private Team team;

    public Member(String name, Team team) {
        this.name = name;
        this.team = team;
    }

    // 1안
    public String getTeamName() {
        return team.name;
    }

    // 2안
    public String getTeamGetName() {
        return team.getName();
    }
}

 

이제 검증을 시작하겠다.

먼저 Team 클래스를 정의한다.

📌 Team.class

@Entity
@Getter
@NoArgsConstructor
public class Team {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    String name;

    public Team(String name) {
        this.name = name;
    }
}

 

그리고 Member 클래스를 정의한다.

📌 Member.class

@Entity
@Getter
@NoArgsConstructor
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    private Team team;

    public Member(String name, Team team) {
        this.name = name;
        this.team = team;
    }

    // 1안
    public String getTeamName() {
        return team.name;
    }

    // 2안
    public String getTeamGetName() {
        return team.getName();
    }
}

Member 엔티티에서 Team 엔티티는 지연로딩 설정했고 영속성 전이설정까지 했다.
Member 엔티티에서 Team 엔티티의 이름을 가져올 때 '1안'에서는 필드값 접근인 team.name으로 가져온다.
'2안'에서는 team.getName()으로 값을 가져온다.

 

📌 MemberTests.class

@DataJpaTest
class MemberTests {

    @Autowired
    private MemberRepository memberRepository;

    @Autowired
    private TeamRepository teamRepository;

    @PersistenceContext
    private EntityManager em;

    @Test
    void 팀_이름_조회_테스트() {
        Team teamA = new Team("teamA");

        Member member1 = new Member("이름1", teamA);

        Member savedMember = memberRepository.save(member1);
        Long id = savedMember.getId();

        em.flush();
        em.clear();

        System.out.println("=========================================");

        Member findMember1 = memberRepository.findById(id).orElse(null);
        System.out.println();

        System.out.println("멤버가 속한 팀 조회 : " + findMember1.getTeam().getClass().getName());
        System.out.println();

        System.out.println("멤버가 속한 팀의 이름 조회 1안 : " + findMember1.getTeamName());
        System.out.println();

        System.out.println("멤버가 속한 팀의 이름 조회 2안 : " + findMember1.getTeamGetName());
        System.out.println("=========================================");
    }
}
=========================================
Hibernate: select member0_.id as id1_0_0_, member0_.name as name2_0_0_, member0_.team_id as team_id3_0_0_ from member member0_ where member0_.id=?

멤버가 속한 팀 조회 : hello.refactor.prac1.Team$HibernateProxy$Xww9CYjY

멤버가 속한 팀의 이름 조회 1안 : null

Hibernate: select team0_.id as id1_1_0_, team0_.name as name2_1_0_ from team team0_ where team0_.id=?
멤버가 속한 팀의 이름 조회 2안 : teamA
=========================================

결론부터 말하자면 2안인 team.getName()으로 가져와야 한다.

테스트 진행 순서 
1. 먼저 teamA를 member1에 생성자 주입을 하고 영속성 전이로 member1이 영속화 될 때 teamA도 같이 영속화 시킨다.
2 .영속성 컨텍스트를 비워주기 위해서 em.flush()와 em.clear()를 날린다.
3. db에서 member1를 조회한다.
4. 조회한 member1에서 teamA을 조회해본다. 결과는 프록시 객체로 나온다.
5. member1에 속한 teamA의 팀 이름을 가져오기 위해서 어떤 방법을 사용할지 1안과 2안을 모두 테스트 해본다.
6. 1안을 사용할 때는 team.name 으로 값을 가져오지만 프록시 객체라서 null 값이 나온다.
7. 2안을 사용할 때는 team.getName()으로 값을 조회하므로 콘솔창에서 나오듯 teamA인 프록시 객체가 db에서 실제 엔티티에 접근하기 때문에 select 쿼리가 나간다.이 때 참고할 점이 프록시 객체가 실제  엔티티로 바뀌지 않는다. 영속성 컨텍스트 1차 캐시 때문에 프록시 객체가 실제 엔티티로 접근 하는 것이다. 고로 2안을 사용했을 때는 프록시가 실제 엔티티에 접근하여 DB에 있는 값을 제대로 가져올 수 있다.

이러한 관점으로 봤을 때 엔티티의 equals() 메서드와 hashCode() 메서드를 오버라이딩할 때 프록시를 대비하여 get 방식으로 사용해야 안전하다.

예시)

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    AddressEmd addressEmd = (AddressEmd) o;
    return Objects.equals(getCity(), addressEmd.getCity())
            && Objects.equals(getStreet(), addressEmd.getStreet())
            && Objects.equals(getZipcode(), addressEmd.getZipcode());
}

@Override
public int hashCode() {
    return Objects.hash(getCity(), getStreet(), getZipcode());
}

 

결론 : 프록시 객체를 대비하여 get 방식으로 가져올 것을 추천한다!

 

 

728x90

'[JPA] > JPA' 카테고리의 다른 글

[JPA] @DataJpaTest + 테스트 DB 변경  (0) 2022.05.15
[JPA] N+1 문제 (즉시 로딩 / 지연 로딩 / 일반 Join / Fetch Join)  (0) 2022.05.02
[JPA] 엔티티의 필드 컬렉션을 생성과 동시에 초기화 하는 이유  (0) 2022.04.06
    '[JPA]/JPA' 카테고리의 다른 글
    • [JPA] @DataJpaTest + 테스트 DB 변경
    • [JPA] N+1 문제 (즉시 로딩 / 지연 로딩 / 일반 Join / Fetch Join)
    • [JPA] 엔티티의 필드 컬렉션을 생성과 동시에 초기화 하는 이유
    쿠릉쿠릉 쾅쾅
    쿠릉쿠릉 쾅쾅
    깃허브 주소 : https://github.com/kureung

    티스토리툴바