Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- JPQL
- springdatajpa
- Thymeleaf
- 스프링 핵심 원리
- 그리디
- 자바
- 스프링 핵심 기능
- Servlet
- AOP
- QueryDSL
- spring
- 스프링
- Proxy
- SpringBoot
- 알고리즘
- JDBC
- http
- pointcut
- Exception
- 인프런
- Greedy
- 김영한
- 백준
- transaction
- Android
- Spring Boot
- db
- kotlin
- java
- jpa
Archives
- Today
- Total
개발자되기 프로젝트
지연로딩과 조회성능 최적화(엔티티 노출) 본문
- 주문, 배송정보, 회원을 조회하는 api를 만들자
- 지연로딩 때문에 발생하는 "성능"문제를 단계적으로 해결!!!!!
1. 주문조회 : 엔티티를 직접 노출, X To One 성능 최적화
/**
* 연관관계(x to One)
* Order-> Member (N:1)
* Order -> Delivery (1:1)
*/
@RestController
@RequiredArgsConstructor
public class OrderSimpleApiController {
private final OrderRepository orderRepository;
@GetMapping("/api/v1/simple-orders")
public List<Order>ordersV1(){
List<Order> all = orderRepository.findAllByString(new OrderSearch());
return all;
}
}
- 아앗....멈추지 않는다...무한루프에 빠졌다...
- 먼저 Order와 Member는 양방향 관계이다.
- Order를 불러오면 Member를 불러와야 하는데 Member안에 orderList가 있고 또 order는 불러오고 순환참조...
- 따라서 json을 만드는 jackson 라이브러리는 계속 순환참조를 할 수 밖에 없음..
- 따라서 양방향 연관관계에서 Jacson이 헷갈리지 않게 한쪽은 @JasonIgnore을 적용해주자.
- localhost:8080/api/v1/simple-orders 을 실행해보자.
- 앗...그래도 에러가 발생한다..bytebuddy가 뭐지..?
Type definition error: [simple type, class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor]
- 왜 발생할까?
- Order에서 Member를 가지고 올 때 지연로딩이 적용된다
- 즉 Db에서 가지고 올 때 order 만 가지고 오고,
- member는 프록시 라이브러리를 사용하여 프록시 객체를 가져온다.
- 이 때 위의 bytebuddy가 프록시 라이브러리를 의미함.
- 최근 프록시 객체를 만들 때 bytebuddy 라이브러리를 많이 사용함.
- 즉 Member 는 사실상 byteBuddyIntercaptor가 들어가있는 것.
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "MEMBER_ID")
private Member member = new ByteBuddyInterceptor();
- member의 값을 알기위해 프록시 객체에 접근하면 그 때 db에 쿼리를 날려서 진짜 member를 가져옴.
- = 프록시 초기화
- 그런데 여기의 문제는 jacson 라이브러리로 json으로 만들어야 하는데, member가 member가 아니고 프록시네..?
- json으로 못만들어서 발생한 문제.
- 그러면 jackson 라이브러리에 지연로딩이 적용되는 객체는 json으로 뿌리지 말라하면됨.
- 위에서 언급한 것곽 타이ㅣ jackson라이브러리에 지연로딩이 적영되는 객체는
- json으로 뿌리지 않도록 지정하기 위해 Hibernate5Module사용이 필요하다.
- dependency 등록
// https://mvnrepository.com/artifact/com.fasterxml.jackson.datatype/jackson-datatype-hibernate5
implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-hibernate5'
- Hibernate5Module을 빈으로 등록
@SpringBootApplication
public class SpringJpa2Application {
public static void main(String[] args) {
SpringApplication.run(SpringJpa2Application.class, args);
}
@Bean
Hibernate5Module hibernate5Module(){
return new Hibernate5Module();
}
}
- Hibernate5Module을 빈으로 등록하면 지연로딩 객체는 json으로 변환하지 않고 리턴하게된다.
- 지연로딩 객체는 아직 db에서 가져오지 않았기 때문에 null로 반환된다.
- 지연로딩이라도 값을 불러올 수 있긴 하다.
- FORCE_LAZY_LOADING : LAZY로딩 강제로 실행.
@SpringBootApplication
public class SpringJpa2Application {
public static void main(String[] args) {
SpringApplication.run(SpringJpa2Application.class, args);
}
@Bean
Hibernate5Module hibernate5Module(){
Hibernate5Module hibernate5Module = new Hibernate5Module();
hibernate5Module.configure(Hibernate5Module.Feature.FORCE_LAZY_LOADING, true);
return new Hibernate5Module();
}
}
- 근데 이처럼 엔티티를 직접 사용하면 안됨 ㅋㅋㅋㅋ
- 불필요한 정보도 노출되고 불필요한 쿼리로 인해 성능문제도 발생함.
- 그렇담 FORCE_LAZY_LOADING을 쓰지 않고 원하는 정보만 출력해 보자.
- 프록시를 강제로 초기화하자.
- get으로 member 정보에 접근하면 쿼리가 날라가서 member정보를 가져온다.
@GetMapping("/api/v1/simple-orders")
public List<Order>ordersV1(){
List<Order> all = orderRepository.findAllByString(new OrderSearch());
for (Order order : all) {
order.getMember().getName(); //프록시 강제 초기화.
order.getDelivery();
}
return all;
}
- 강제로 초기화한 member와, delivery만 초기화가 되었다.
하지만 이래도 불필요한 정보가 너무 많이 노출된다...
진짜로 엔티티를 API응답으로 외부로 노출하지 말자..
DTO로 변환해서 반환하자!!!!!
GitHub : 210822
https://github.com/bsh6463/SpringBootJPA1
'인프런 > [인프런] Springboot와 JPA활용 2' 카테고리의 다른 글
지연 로딩과 조회 성능 최적화(JPA에서 DTO 바로 조회) (0) | 2021.08.23 |
---|---|
지연 로딩과 조회 성능 최적화(FETCH JOIN) (0) | 2021.08.22 |
지연로딩과 성능최적화(DTO, N+1) (0) | 2021.08.22 |
API 개발 고급 소개 1 (0) | 2021.08.22 |
회원 등록, 수정, 조회 API (0) | 2021.08.21 |
Comments