고급 주제와 성능 최적화

원종서·2022년 2월 10일
1

JPA

목록 보기
2/13

엔티티 비교

영속성 컨텍스트에는 엔티티 인스턴스를 보관하기 위한 1차 캐시 저장소가 있다.
1차 캐시는 영속성 컨텍스트와 생명주기를 같이한다.
엔티티를 1차 캐시에 저장하기 때문에 데베와의 연결을 최소화 할 수 있게 된다.

spring data jpa 는 트랜잭션의 주기와 영속성 컨텍스트 생명주기를 같이한다.
같은 트랜잭션 (영속성 컨텍스트) 에서 조회한 엔티티는 동등성 뿐아니라 동일성도 보장한다.

-- 롤백 시 flush 안함.

프록시

프록시를 먼저 조회하고 , 원본 엔티티를 조회하면 1차 캐시에 있는 원본 엔티티에 프록시 인스턴스의 참조값을 대입힌다.
원본 엔티티를 먼저 조회하고 프록시를 조회하면 1차 캐시에 있는 프록시에 원본 엔티티의 참조값을 대입힌다.

따라서 영속성 컨텍스트는 자신이 관리하는 영속 엔티티의 동일성을 보장한다.

프록시 타입 비교

프록시는 원본 엔티티를 상속받아 만들어지기 때문에 , 프록시로 조회한 엔티티의 타입을 비교할 때는 "==" 연산자가 아니라 instanceof 연산자를 사용해야한다.

프록시 동등성 비교

엔티티의 동등성을 비교하려면 비즈니스 키를 사용해 equals 메서드를 오버라이딩해서 비교하면 된다.
하지만 IDE 등으로 오버라이딩 할 시 원본 엔티티라면 문제 없지만 프록시면 문제가 발생 할 수 있다

@Entity
public class Member {
	@Id
    Long id;
    String name;
    
    getter , setter ..
    
    @Override
    public boolean equals(Object obj){
    	if(this == obj) return true;
        if(obj == null) return false;
        if(this.getClass() != ojb.getClass()) return false; // 1.
        
        Member member = (Member) obj;
        
        if(name != null ?  !name.eqauls(member.name) : member.name != null)
        	return false; //2.
        return true;
	}

위처럼 equals 메서드를 오버라딩해서 원본 엔티티와 프록시 객체를 비교하면 제대로 된 값이 반환되지 않는다.
이유는 1, 2번의 이유이다.

1번의 문제는 앞서 말한 것과 같이 프록시 객체는 원본 객체를 상속 받은 것임으로 instanceof 연산으로 비교해야만 정확한값을 도출해낼 수 있다.
2번의 문제는 프록시 객체는 내부에 target 속성에 원본 엔티티의 참조값'만'을 갖고 있다. 나머지 필드의 값들은 갖고 있지 않은데 위의 코드처럼 member.name 을 호출하면 null값이 반환되고 원하는 값을 원하지 못하게 된다.

    
    @Override
    public boolean equals(Object obj){
    	if(this == obj) return true;
        if(obj == null) return false;
		if(!(obj instanceof Member)) return false;
        
        Member member = (Member) obj;
        
        if(name != null ?  !name.eqauls(member.getName()) : member.getName() != null)
        	return false;
        return true;
	}

이 코드가 원본 엔티티와 프록시 객체를 정확하게 비교하는 코드이다.
참고로 프록시 객체의 .get*() 메서드를 호출하는 즉시 초기화가 발생하고 필드에 값들이 채원진다.

상속관계와 프록시

0개의 댓글