• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    미해결

Repository, Service에 따른 , @Transaction(readOnly=ture)에 따른 쿼리 차이에 대해 질문드립니다.

22.05.12 22:22 작성 조회수 409

0

@Entity
@Getter @Setter
public class Category {
    @Id @Column(name="CATEGORY_ID")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="MEMBER_ID")
    private Member member;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="PARENT_ID")
    private Category parent;

    @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
    private List<Category> child = new ArrayList<>();

}
@Repository
public class CategoryRepository {

    private final EntityManager em;
    
        public List<Category> findAll(){
        return em.createQuery("select c from Category c", Category.class)
                .getResultList();
    }
}
@Service
@Transactional
@RequiredArgsConstructor
public class CategoryService {

    private final CategoryRepository categoryRepository;
    
@Transaction <-- 이부분
//@Transaction(readOnly=true) <-- 이부분 public List<Category> findAll() {return categoryRepository.findAll(); } }
@RestController
@RequiredArgsConstructor
public class CategoryApiController {
    private final CategoryService categoryService;
    private final CategoryRepository categoryRepository;
    private final MemberService memberService;

    @GetMapping("/api/v1/categories")
    public Result getCategory(@RequestParam(required = false, value = "id") String memberId){

            List<Category> categories = categoryService.findAll(); <-- 이부분
// List<Category> categories = categoryRepository.findAll(); <-- 이부분
List<CategoryDto> categoryDtos = categories.stream() .map(c -> new CategoryDto(c)) .collect(Collectors.toList()); return new Result(categoryDtos.size(), categoryDtos); } @Data @AllArgsConstructor static class CategoryDto{ public CategoryDto(Long id, String name) { this.id = id; this.name = name; } public CategoryDto(Category category) { this.id = category.getId(); this.name = category.getName(); this.child = category.getChild().stream() .map(c -> new CategoryDto(c.getId(),c.getName())) .collect(Collectors.toList()); } private Long id; private String name; private List<CategoryDto> child; private Long parentId; } }
  1. categoryRepository.findAll()를 호출 했을 때
2022-05-12 23:35:46.474 DEBUG 18928 --- [nio-9090-exec-2] o.j.s.OpenEntityManagerInViewInterceptor : Opening JPA EntityManager in OpenEntityManagerInViewInterceptor
2022-05-12 23:35:46.527  INFO 18928 --- [nio-9090-exec-2] p6spy                                    : #1652366146527 | took 0ms | statement | connection 1| url jdbc:h2:tcp://localhost/~/blog
select category0_.category_id as category1_0_, category0_.member_id as member_i3_0_, category0_.name as name2_0_, category0_.parent_id as parent_i4_0_ from category category0_
select category0_.category_id as category1_0_, category0_.member_id as member_i3_0_, category0_.name as name2_0_, category0_.parent_id as parent_i4_0_ from category category0_;
2022-05-12 23:35:46.539  INFO 18928 --- [nio-9090-exec-2] p6spy                                    : #1652366146539 | took 0ms | statement | connection 1| url jdbc:h2:tcp://localhost/~/blog
select child0_.parent_id as parent_i4_0_1_, child0_.category_id as category1_0_1_, child0_.category_id as category1_0_0_, child0_.member_id as member_i3_0_0_, child0_.name as name2_0_0_, child0_.parent_id as parent_i4_0_0_ from category child0_ where child0_.parent_id in (?, ?, ?, ?, ?, ?, ?, ?)
select child0_.parent_id as parent_i4_0_1_, child0_.category_id as category1_0_1_, child0_.category_id as category1_0_0_, child0_.member_id as member_i3_0_0_, child0_.name as name2_0_0_, child0_.parent_id as parent_i4_0_0_ from category child0_ where child0_.parent_id in (6, 7, 8, 9, 10, 11, 12, 13);
2022-05-12 23:35:46.566 DEBUG 18928 --- [nio-9090-exec-2] o.j.s.OpenEntityManagerInViewInterceptor : Closing JPA EntityManager in OpenEntityManagerInViewInterceptor
  1. categoryService.findAll() 을 호출 했을 때

1)@Transaction(readOnly = true)

2022-05-12 23:31:45.235 DEBUG 18388 --- [nio-9090-exec-1] o.j.s.OpenEntityManagerInViewInterceptor : Opening JPA EntityManager in OpenEntityManagerInViewInterceptor
2022-05-12 23:31:45.258 DEBUG 18388 --- [nio-9090-exec-1] o.s.orm.jpa.JpaTransactionManager        : Found thread-bound EntityManager [SessionImpl(72969494<open>)] for JPA transaction
2022-05-12 23:31:45.258 DEBUG 18388 --- [nio-9090-exec-1] o.s.orm.jpa.JpaTransactionManager        : Creating new transaction with name [com.blog.demo.service.CategoryService.findAllRootCategories]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly
2022-05-12 23:31:45.261 DEBUG 18388 --- [nio-9090-exec-1] o.s.orm.jpa.JpaTransactionManager        : Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@675de2b1]
2022-05-12 23:31:45.292  INFO 18388 --- [nio-9090-exec-1] p6spy                                    : #1652365905292 | took 0ms | statement | connection 1| url jdbc:h2:tcp://localhost/~/blog
select category0_.category_id as category1_0_, category0_.member_id as member_i3_0_, category0_.name as name2_0_, category0_.parent_id as parent_i4_0_ from category category0_
select category0_.category_id as category1_0_, category0_.member_id as member_i3_0_, category0_.name as name2_0_, category0_.parent_id as parent_i4_0_ from category category0_;
2022-05-12 23:31:45.301 DEBUG 18388 --- [nio-9090-exec-1] o.s.orm.jpa.JpaTransactionManager        : Initiating transaction commit
2022-05-12 23:31:45.301 DEBUG 18388 --- [nio-9090-exec-1] o.s.orm.jpa.JpaTransactionManager        : Committing JPA transaction on EntityManager [SessionImpl(72969494<open>)]
2022-05-12 23:31:45.301  INFO 18388 --- [nio-9090-exec-1] p6spy                                    : #1652365905301 | took 0ms | commit | connection 1| url jdbc:h2:tcp://localhost/~/blog

;
2022-05-12 23:31:45.302 DEBUG 18388 --- [nio-9090-exec-1] o.s.orm.jpa.JpaTransactionManager        : Not closing pre-bound JPA EntityManager after transaction
2022-05-12 23:31:45.303  INFO 18388 --- [nio-9090-exec-1] p6spy                                    : #1652365905303 | took 0ms | statement | connection 1| url jdbc:h2:tcp://localhost/~/blog
select child0_.parent_id as parent_i4_0_1_, child0_.category_id as category1_0_1_, child0_.category_id as category1_0_0_, child0_.member_id as member_i3_0_0_, child0_.name as name2_0_0_, child0_.parent_id as parent_i4_0_0_ from category child0_ where child0_.parent_id in (?, ?, ?, ?, ?, ?, ?, ?)
select child0_.parent_id as parent_i4_0_1_, child0_.category_id as category1_0_1_, child0_.category_id as category1_0_0_, child0_.member_id as member_i3_0_0_, child0_.name as name2_0_0_, child0_.parent_id as parent_i4_0_0_ from category child0_ where child0_.parent_id in (6, 7, 8, 9, 10, 11, 12, 13);
2022-05-12 23:31:45.329 DEBUG 18388 --- [nio-9090-exec-1] o.j.s.OpenEntityManagerInViewInterceptor : Closing JPA EntityManager in OpenEntityManagerInViewInterceptor

2)@Transaction

2022-05-12 23:37:14.348 DEBUG 19206 --- [nio-9090-exec-2] o.j.s.OpenEntityManagerInViewInterceptor : Opening JPA EntityManager in OpenEntityManagerInViewInterceptor
2022-05-12 23:37:14.369 DEBUG 19206 --- [nio-9090-exec-2] o.s.orm.jpa.JpaTransactionManager        : Found thread-bound EntityManager [SessionImpl(1468362384<open>)] for JPA transaction
2022-05-12 23:37:14.369 DEBUG 19206 --- [nio-9090-exec-2] o.s.orm.jpa.JpaTransactionManager        : Creating new transaction with name [com.blog.demo.service.CategoryService.findAllRootCategories]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2022-05-12 23:37:14.371 DEBUG 19206 --- [nio-9090-exec-2] o.s.orm.jpa.JpaTransactionManager        : Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@4209a22e]
2022-05-12 23:37:14.402  INFO 19206 --- [nio-9090-exec-2] p6spy                                    : #1652366234402 | took 0ms | statement | connection 1| url jdbc:h2:tcp://localhost/~/blog
select category0_.category_id as category1_0_, category0_.member_id as member_i3_0_, category0_.name as name2_0_, category0_.parent_id as parent_i4_0_ from category category0_
select category0_.category_id as category1_0_, category0_.member_id as member_i3_0_, category0_.name as name2_0_, category0_.parent_id as parent_i4_0_ from category category0_;
2022-05-12 23:37:14.414 DEBUG 19206 --- [nio-9090-exec-2] o.s.orm.jpa.JpaTransactionManager        : Initiating transaction commit
2022-05-12 23:37:14.414 DEBUG 19206 --- [nio-9090-exec-2] o.s.orm.jpa.JpaTransactionManager        : Committing JPA transaction on EntityManager [SessionImpl(1468362384<open>)]
2022-05-12 23:37:14.418  INFO 19206 --- [nio-9090-exec-2] p6spy                                    : #1652366234418 | took 0ms | commit | connection 1| url jdbc:h2:tcp://localhost/~/blog

;
2022-05-12 23:37:14.419 DEBUG 19206 --- [nio-9090-exec-2] o.s.orm.jpa.JpaTransactionManager        : Not closing pre-bound JPA EntityManager after transaction
2022-05-12 23:37:14.420  INFO 19206 --- [nio-9090-exec-2] p6spy                                    : #1652366234420 | took 0ms | statement | connection 1| url jdbc:h2:tcp://localhost/~/blog
select child0_.parent_id as parent_i4_0_1_, child0_.category_id as category1_0_1_, child0_.category_id as category1_0_0_, child0_.member_id as member_i3_0_0_, child0_.name as name2_0_0_, child0_.parent_id as parent_i4_0_0_ from category child0_ where child0_.parent_id=?
select child0_.parent_id as parent_i4_0_1_, child0_.category_id as category1_0_1_, child0_.category_id as category1_0_0_, child0_.member_id as member_i3_0_0_, child0_.name as name2_0_0_, child0_.parent_id as parent_i4_0_0_ from category child0_ where child0_.parent_id=6;
2022-05-12 23:37:14.421  INFO 19206 --- [nio-9090-exec-2] p6spy                                    : #1652366234421 | took 0ms | statement | connection 1| url jdbc:h2:tcp://localhost/~/blog
select child0_.parent_id as parent_i4_0_1_, child0_.category_id as category1_0_1_, child0_.category_id as category1_0_0_, child0_.member_id as member_i3_0_0_, child0_.name as name2_0_0_, child0_.parent_id as parent_i4_0_0_ from category child0_ where child0_.parent_id=?
select child0_.parent_id as parent_i4_0_1_, child0_.category_id as category1_0_1_, child0_.category_id as category1_0_0_, child0_.member_id as member_i3_0_0_, child0_.name as name2_0_0_, child0_.parent_id as parent_i4_0_0_ from category child0_ where child0_.parent_id=7;
2022-05-12 23:37:14.423  INFO 19206 --- [nio-9090-exec-2] p6spy                                    : #1652366234423 | took 0ms | statement | connection 1| url jdbc:h2:tcp://localhost/~/blog
select child0_.parent_id as parent_i4_0_1_, child0_.category_id as category1_0_1_, child0_.category_id as category1_0_0_, child0_.member_id as member_i3_0_0_, child0_.name as name2_0_0_, child0_.parent_id as parent_i4_0_0_ from category child0_ where child0_.parent_id=?
select child0_.parent_id as parent_i4_0_1_, child0_.category_id as category1_0_1_, child0_.category_id as category1_0_0_, child0_.member_id as member_i3_0_0_, child0_.name as name2_0_0_, child0_.parent_id as parent_i4_0_0_ from category child0_ where child0_.parent_id=8;
2022-05-12 23:37:14.424  INFO 19206 --- [nio-9090-exec-2] p6spy                                    : #1652366234424 | took 0ms | statement | connection 1| url jdbc:h2:tcp://localhost/~/blog
select child0_.parent_id as parent_i4_0_1_, child0_.category_id as category1_0_1_, child0_.category_id as category1_0_0_, child0_.member_id as member_i3_0_0_, child0_.name as name2_0_0_, child0_.parent_id as parent_i4_0_0_ from category child0_ where child0_.parent_id=?
select child0_.parent_id as parent_i4_0_1_, child0_.category_id as category1_0_1_, child0_.category_id as category1_0_0_, child0_.member_id as member_i3_0_0_, child0_.name as name2_0_0_, child0_.parent_id as parent_i4_0_0_ from category child0_ where child0_.parent_id=9;
2022-05-12 23:37:14.424  INFO 19206 --- [nio-9090-exec-2] p6spy                                    : #1652366234424 | took 0ms | statement | connection 1| url jdbc:h2:tcp://localhost/~/blog
select child0_.parent_id as parent_i4_0_1_, child0_.category_id as category1_0_1_, child0_.category_id as category1_0_0_, child0_.member_id as member_i3_0_0_, child0_.name as name2_0_0_, child0_.parent_id as parent_i4_0_0_ from category child0_ where child0_.parent_id=?
select child0_.parent_id as parent_i4_0_1_, child0_.category_id as category1_0_1_, child0_.category_id as category1_0_0_, child0_.member_id as member_i3_0_0_, child0_.name as name2_0_0_, child0_.parent_id as parent_i4_0_0_ from category child0_ where child0_.parent_id=10;
2022-05-12 23:37:14.424  INFO 19206 --- [nio-9090-exec-2] p6spy                                    : #1652366234424 | took 0ms | statement | connection 1| url jdbc:h2:tcp://localhost/~/blog
select child0_.parent_id as parent_i4_0_1_, child0_.category_id as category1_0_1_, child0_.category_id as category1_0_0_, child0_.member_id as member_i3_0_0_, child0_.name as name2_0_0_, child0_.parent_id as parent_i4_0_0_ from category child0_ where child0_.parent_id=?
select child0_.parent_id as parent_i4_0_1_, child0_.category_id as category1_0_1_, child0_.category_id as category1_0_0_, child0_.member_id as member_i3_0_0_, child0_.name as name2_0_0_, child0_.parent_id as parent_i4_0_0_ from category child0_ where child0_.parent_id=11;
2022-05-12 23:37:14.425  INFO 19206 --- [nio-9090-exec-2] p6spy                                    : #1652366234425 | took 0ms | statement | connection 1| url jdbc:h2:tcp://localhost/~/blog
select child0_.parent_id as parent_i4_0_1_, child0_.category_id as category1_0_1_, child0_.category_id as category1_0_0_, child0_.member_id as member_i3_0_0_, child0_.name as name2_0_0_, child0_.parent_id as parent_i4_0_0_ from category child0_ where child0_.parent_id=?
select child0_.parent_id as parent_i4_0_1_, child0_.category_id as category1_0_1_, child0_.category_id as category1_0_0_, child0_.member_id as member_i3_0_0_, child0_.name as name2_0_0_, child0_.parent_id as parent_i4_0_0_ from category child0_ where child0_.parent_id=12;
2022-05-12 23:37:14.425  INFO 19206 --- [nio-9090-exec-2] p6spy                                    : #1652366234425 | took 0ms | statement | connection 1| url jdbc:h2:tcp://localhost/~/blog
select child0_.parent_id as parent_i4_0_1_, child0_.category_id as category1_0_1_, child0_.category_id as category1_0_0_, child0_.member_id as member_i3_0_0_, child0_.name as name2_0_0_, child0_.parent_id as parent_i4_0_0_ from category child0_ where child0_.parent_id=?
select child0_.parent_id as parent_i4_0_1_, child0_.category_id as category1_0_1_, child0_.category_id as category1_0_0_, child0_.member_id as member_i3_0_0_, child0_.name as name2_0_0_, child0_.parent_id as parent_i4_0_0_ from category child0_ where child0_.parent_id=13;
2022-05-12 23:37:14.456 DEBUG 19206 --- [nio-9090-exec-2] o.j.s.OpenEntityManagerInViewInterceptor : Closing JPA EntityManager in OpenEntityManagerInViewInterceptor

 

안녕하세요. 좋은 강의 감사합니다.

강의에서 Order로 api를 만들어주셔서 저는 개인적으로 Category에 대해서 api를 간단하게 만들어보면서 테스트를 몇가지 진행했었는데요.

Controller에서 Category의 child를 지연로딩을 통해서 가져올 때 아래 세가지의 경우에 따라 쿼리문이 달라지는 현상을 겪었습니다. 위 코드에서 `<-- 이부분` 으로 표시된 부분을 참고해주시면 감사하겠습니다.

1. Repository로 최초에 Category를 가져올 때에는 in 쿼리가 잘 나갔고,

2. Controller에서 Service로 최초에 Category를 가져올 때는 Service의 해당 메소드에 @Transaction 일떄에는 N+1 문제가 발생하고

3. readOnly=true 일때에는 in 쿼리로 잘 가지고 오는데

이 상황에대해서 이해가 잘 되지 않아 질문 드리게 됐습니다.

3가지 상황 모두 OSIV가 켜져있고, default_batch_fetch_size: 100인 상황입니다.

각각 상황에 대해서 왜 in 쿼리가 나가고 안나가는지에 대해서 설명해주실 수 있을까요? 감사합니다.

 

Repository와 Service차이의 경우 OSIV가 켜져있기 때문에 Controller에 돌아와서도 영속성 컨텍스트가 유지되는게 동일 할텐데 왜 쿼리 차이가 있는지 모르겠고 

readOnly=true인 경우, 영속성 컨텍스트의 내용을 플러시하지 않는 것으로 알고있는데 플러시 여부와 지연로딩시 쿼리가 다르게 나가는 것은 상관이 없을 것 같은데 왜 차이가 나는지 파악이 안되고 있습니다.

 

* 트랜잭션 로그를 찍어보니 Repository에서는 transaction을 생성하지 않고 트랜잭션없는 읽기를 진행하는 것으로 보이고 Service에서는 readOnly인 트랜잭션을 생성하냐/아니냐의 차이로 보입니다.

답변 1

답변을 작성해보세요.

0

안녕하세요. 나님

다음을 참고해주세요^^

https://www.inflearn.com/questions/427216

감사합니다.