Loading...

Spring / / 2022. 5. 10. 10:29

EntityManager 영속화 비영속화

반응형

DTO에 엔티티를 담는 것은 좋지 못하다.

서비스단에서 엔티티의 모든 데이터를 dto에 옮기는 게 정석이다.

dto는 엔티티와 연관이 있으면 안된다!

 

JPA를 사용하다가 lazy 로딩 때문에 머리 아파질 일이 반드시 생긴다.

 

예를 들어 Post를 SELECT 한다고 해보자.

 

이때 Post와 연관되어 있는 User와 Category의 ManyToOne 기본전략이 EAGER이기 때문에

기본적으로 요청 시에 JOIN 해서 들고 온다.

 

카테고리와 유저를 담아서 영속화된 엔티티를 쭉쭉쭉 컨트롤러까지 보낸다.

 

 

이때 컨트롤러에서 이 Post 엔티티를 리턴한다면

메세지 컨버터가 Post 오브젝트의 getter를 모두 때리며 json으로 변환한다.

 

User의 getter를 때릴 때는 문제가 없지만,

Category의 getter를 때릴때 또 유저의 getter를 때리게 되어 Lazy loading이 일어난다.

 

유저를 이미 받아왔는데 쓸데없이 2개를 들고 오게 된 것이다.

 

불필요한 Lazy loading을 막기 위해 엔티티를 리턴하지 않는다.

dto를 리턴할 때도 dto 내부에 엔티티를 넣지 않고

엔티티와 내부가 똑같이 생긴 dto를 생성하여 리턴해준다.

 

이렇게 해야 영속성이 끊기기 때문이다.

 

 


 

영속성을 끊는 방법은 2가지가 있다.

 

1. 서비스단에서 Dto로 모두 변환하여 리턴

Post 오브젝트와 내부가 똑같이 생긴 PostDto를 만든다.

User와 Category도 마찬가지로 Dto를 만든다.

 

영속성 컨텍스트나 레파지토리를 직접 건드릴 수 없기 때문에

서비스단에서 엔티티를 모두 Dto로 변환해야 컨트롤러에서 어떠한 변수도 발생하지 않는다.

 

2. 컨트롤러에서 영속성 제거

영속화 된 엔티티를 컨트롤러에 가져와서

메세지 컨버터가 getter를 때릴 때 문제가 발생하기 때문에

Post의 영속성을 제거해준다.

 

영속성 컨텍스트에서 Post 엔티티를 제거하면

Lazy loading시에 다시 SELECT가 일어나지 않는다.

 

이를 비영속화라고 한다.

 

비영속화는 detach( ) 메서드를 사용하는데 이는 EntityManager가 가지고 있다.

 

 

EntityManager는 prepareStatement와 같은 것이다.

IoC 컨테이너에 등록되어있기 때문에 DI가 가능하다.

private final EntityManager em; // IoC 컨테이너에서 가져옴

 

Hibernate.initialize( )는 혹시나 컨트롤러에서 LAZY loading 하다가

어떤 오류가 발생할지 모르니까 Lazy loading을 미리 하는 방식이다.

 

필요한 데이터를 미리 초기화시키는 것!

서비스단에서 데이터를 미리 만들어두는 것!

 

서비스에서 영속화 된 엔티티에 기본전략이 Lazy로 연관되어있는 데이터를

컨트롤러에서 리턴시에 메세지 컨버터가 getter를 때려 채울 때 혹시나 문제가 발생할까봐 미리 채우는 방법이다.

 

// 미리 필요한 애들을 다 땡겨내림 - Lazy loading 미리하기
Hibernate.initialize(postEntity);

 


 

영속화

em.persist(postEntity)

 

실제로 findById 내부에 em.persist가 있는 것이다.

Love love = new Love();
em.persist(love); // SELECT 하지 않아도 영속성 컨텍스트에 넣을 수 있음

 

 

비영속화

em.detach(postEntity)

 em.remove(postEntity)

 

detach( )는 영속성 컨텍스트에서 잠깐 빼두는 것이고

merge( ) 메서드를 통해 다시 넣어줄 수 있다.

영속성 컨텍스트에 넣어주면 DB와 다시 연결이 가능한 상태인 것이다.

 

// 영속화 비영속화
public Love emTest2() {
    Love love = new Love();
    em.persist(love); // 영속화
    em.detach(love); // 비영속화
    em.merge(love); // 재 영속화
    em.remove(love); // 영속성 삭제
    return love; // MessageConverter
}

 

 remove( )는 DB의 연결까지 끊어버리는 상태를 말한다.

 

 

 

컨트롤러에서 비영속화시킨 엔티티를 리턴할 때 메세지 컨버터가 getter를 때려도 상관없다.

비영속화 되어있어서 Lazy loading을 하지 않으니까!

em.detach(love);
return love; // MessageConverter가 getter 때릴 때도 상관없다. 비영속화시켰으니까!

 


 

복잡한 쿼리들이 길어지면 컴파일 시점에 오류를 발견할 수 없다.

문자열은 실행 시에 오류를 잡을 수 있기 때문이다.

 

컴파일 시점에 쿼리의 오류를 발견하기 위해 QueryDSL 라이브러리를 사용한다.

사용 예시는 다음과 같다.

 

생긴 게 메서드 모양 같다..

Member findMember = queryFactory
                        .select(member)
                        .from(member)
                        .where(member.username.eq("member1"))//파라미터 바인딩 처리
                        .fetchOne();

 

QueryDSL을 사용하지 않은 이유는!!


복잡한 쿼리를 사용할 수 있고, 컴파일 시점에 쿼리 오류도 잡을 수 있고, 동적 쿼리도 짤 수 있지만,

단순 JPQL 만으로도 복잡한 쿼리는 사용할 수 있고, 동적 쿼리도 짤 수 있고 ,

무엇보다 기본 SQL을 더 공부할 수 있기 때문에 사용하지 않는다.

 

 

[출처]

 

https://cafe.naver.com/metacoding

 

메타코딩 : 네이버 카페

코린이들의 궁금증

cafe.naver.com

메타 코딩 유튜브

https://www.youtube.com/c/%EB%A9%94%ED%83%80%EC%BD%94%EB%94%A9

 

메타코딩

문의사항 : getinthere@naver.com 인스타그램 : https://www.instagram.com/meta4pm 깃헙 : https://github.com/codingspecialist 유료강좌 : https://www.easyupclass.com

www.youtube.com

 
반응형