JPA 트랜잭션과 Proxy

이광훈·2024년 5월 26일
@Override
    public List<RestaurantListSub> findRestaurantListByTagOrKeyWord(String place , String[] tags , String search , int page) {

        List<Restaurant> query = queryFactory.select(restaurantTag.restaurant)
                .from(restaurantTag)
                .where(restaurantTag.tag.tagName.in(tags))
                .groupBy(restaurantTag.restaurant)
                .having(restaurantTag.restaurant.count().eq((long) tags.length))
                .offset(page)
                .limit(5)
                .fetch();

        List<RestaurantListSub> result = new ArrayList<RestaurantListSub>();

        for (Restaurant item : query){
            List<TagResponse> tagResponseList = new ArrayList<>();

            for(RestaurantTag tag: item.getRestaurantTag()){
                tagResponseList.add(new TagResponse(tag.getTag().getTagName() , "양식"));
            }

            RestaurantListSub rls = new RestaurantListSub();
            rls.setName(item.getName());
            rls.setThumbnail(item.getAddress());
            rls.setTags(tagResponseList);
            rls.setRestaurantId(item.getRestaurantHash());
            result.add(rls);
            System.out.println("rls.toString() = " + rls.toString());
        }

        return result;
    }
  • QueryDSL 을 이용하여 다음과 같은 코드를 짰다. 맨 처음 query 는 queryDSL 을 이용해서 일차적으로 해당 태그들에 해당하는 레스토랑을 뽑는 코드이다
  • 그리고 이후 결과로 나온 각 레스토랑들의 태그들을 뽑아서 가져오는 과정이다. 그리고 현재 restaurant 과 restaurantTag 는 lazy loading 설정이 되어있다.

문제 상황

  • 기능 테스트를 위해 테스트 코드를 작성하여 실행을 하던 중 다음과 같은 에러가 발생했다.

Untitled

  • restaurantTag 에 관련해서 proxy 를 시작할 수 없다고 뜨고 , no session 이라고 한다.

  • Lazy Loading 을 위해서는 proxy 객체가 필요하다. 맨 처음 query 를 이용해 Restaurant 들을 가져올 때, Restaurant 는 내부에 RestaurantTag 객체를 상속받은 proxy 객체를 갖고 있다.

  • 이후 RestaurantTag 가 실제로 사용될 때, 해당 proxy 객체를 이용하여 restaurantTag 의 정보를 가져온다.

  • 하지만 proxy 객체는 영속성 컨텍스트 내에서만 유효하다. 세션이 끝나면 ( Transaction 이 commit 이나 rollback 되면 ) 해당 영속성 컨텍스트 또한 종료되고 그러면 해당 proxy 객체또한 더 이상 사용이 불가능하다.

  • 위 오류가 뜬 이유는 Restaurant 를 가져오는 순간 영속성 컨텍스트가 종료되었기 때문에 프록시 객체인 restaurantTag 를 관리할 수 있는 영속성 컨텍스트가 존재하지 않기 때문이다. 그러면 어떻게 해야할까?

해결방안

  1. Lazy loading 이 아닌 Eager Loading 을 사용한다.
    • Eager Loading 을 사용하면 Restaurant 를 가져오는 순간 바로 restaurantTag 를 가져온다. 하지만 이 방법을 사용하면, restaurant 를 조회하는 다른 api 나 쿼리들에서도 바로 restaurantTag 를 가져오기 때문에 비효율적인 작업들이 많이 수행될 듯 하다.
  2. Transaction 의 종료를 Restaurant 를 가져오는 순간이 아닌 뒤에 restaurantTag 를 가져오는 순간까지 늘린다. 즉 transaction 을 두 작업을 모두 포함하는 범위로 늘리는 것이다.
    • 결국 결론은 service 단이나 repository 단에 transaction annotation 을 붙여주면 된다. 그러면 결국 두 작업이 트랜잭션 처리되어 한 작업으로 묶이고 동일한 영속성 컨텍스트를 사용할 수 있다.
profile
허허,,,

0개의 댓글