30%
61,600원
다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
프록시 관련해서 질문이 있습니다.
=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]영한님 안녕하세요. JPA로드맵, 스프링 로드맵 모두 들으면서 기초 개념을 잡아가고 있습니다. 감사합니다. 다름이 아니라, 학습을 하면서 프록시 객체의 개념이 많이 등장하는데요, 이런 프록시 객체에 대해 궁금한 점이 생겨서 질문드립니다. 스프링이 빈을 싱글톤으로 관리하기 위해 CGLIB 바이트코드 조작을 통해 가짜 프록시 객체를 스프링 빈으로 등록한다고 알고 있습니다. 그리고 스프링 AOP 적용 시에, Pointcut의 대상이 되는 객체(pointcut 대상 메서드가 포함된 객체)에 Advice를 적용하기 위해 CGLIB 또는 JDK 동적 프록시 기술로 프록시 객체를 만든다 배웠습니다. 마지막으로 여기서 JPA에서 지연로딩을 하기 위해 가짜 프록시 객체를 생성하고 실제 프록시 초기화 시점에 DB에서 쿼리를 불러온다고 보았습니다. (--> 이 프록시 객체는 영속성 컨텍스트가 시작될 때 생성되었다가 사라지는 것 같긴 합니다..) 실제로는 더 많은 사례가 있겠지만, 일단 제가 알기로는 이렇게 3가지가 있었던 것 같은데, 이때 생성되는 프록시 객체들은 다 별개의 객체들일까요? 예를 들어 싱글톤 빈으로 등록된 객체가 있는데(CGLIB 프록시), 이 객체가 AOP 적용 대상이라면 CGLIB 혹은 JDK 동적프록시를 통해 또다른 프록시 객체가 생성되는 건지 궁금합니다. 추가로, 지연로딩을 위한 프록시 객체는 영속성 컨텍스트가 시작될 떄 생성되어 영속성 컨텍스트가 종료되면 사라지는 것인지 궁금합니다. 질문이 다소 모호해서 죄송합니다.
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
Result 클래스 관련 질문입니다.
Result 클래스를 만들어서 response 데이터를 보냈습니다. 이때 제너릭으로 설정하신 이유가 있나요? 현재 아래 코드가 이런식입니다. static class Result<T> { private T data; } 그런데 제너릭을 쓰지 않는 반환용 DTO를 만들어서 static class ResultDto{ private List<OrderDto> orderDtos } 이렇게 하지 않는 이유가 있나요? 보통 실무에서는 어떤 방식을 주로 사용하시나요? 감사합니다.
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
JPA 책의 병합 질문입니다!
영한님 항상 좋은 강의 잘 듣고 있습니다! 강의를 들으면서 JPA 책을 함께 공부하던 중 의문점이 생겼습니다. 3장 영속성 관리에서 준영속 상태가 되면 1차 캐시와 쓰기 지연 SQL 저장소에 있는 엔티티의 정보가 제거됩니다. 그런데 준영속 병합 과정에서 merge()를 실행하면 왜 1차 캐시에서 엔티티를 조회하는지 잘 이해가 되지 않습니다. 이미 준영속 상태이거나 비영속 상태이면 1차 캐시에 없다는 것을 예상할 수 있을텐데 굳이 이 과정이 들어간 이유가 있을까요?
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
화면이나 API에 맞춘 서비스
안녕하세요 영한님 OSIV의 마지막 정리 부분에 있는 화면이라는 말이 잘 이해가 안됩니다. - OrderService - OrderService : 핵심 비즈니스 로직 - OrderQueryService : '화면'이나 API에 맞춘 서비스(주로 읽기 전용 트랜잭션 사용) 위에서 얘기하는 '화면'이라는 것과 api는 무엇을 의미하는 건가요? 그리고 실시간 api는 통신량이 많은 고객과의 실시간 api를 말하는 것인가요? 왜냐하면 admin에서도 api를 쓰는데 구별이 잘 안되네요. 감사합니다
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
6분 56초 질문드립니다.
내용중 패키지를 만들고 없애고 해봐도 에러가 계속 납니다. 계속 에러가 나오는데 이유를 모르겠습니다 ㅠ 2022-02-08 10:59:00.744 ERROR 77312 --- [nio-8080-exec-2] o.h.hql.internal.ast.ErrorTracker : Unable to locate appropriate constructor on class [jpabook.jpashop.repository.order.dto.OrderSimpleQueryDto]. Expected arguments are: long, java.lang.String, java.time.LocalDateTime, jpabook.jpashop.domain.OrderStatus, jpabook.jpashop.domain.Address [cause=org.hibernate.PropertyNotFoundException: no appropriate constructor in class: jpabook.jpashop.repository.order.dto.OrderSimpleQueryDto] 2022-02-08 10:59:00.746 ERROR 77312 --- [nio-8080-exec-2] o.h.hql.internal.ast.ErrorTracker : Unable to locate appropriate constructor on class [jpabook.jpashop.repository.order.dto.OrderSimpleQueryDto]. Expected arguments are: long, java.lang.String, java.time.LocalDateTime, jpabook.jpashop.domain.OrderStatus, jpabook.jpashop.domain.Address [cause=org.hibernate.PropertyNotFoundException: no appropriate constructor in class: jpabook.jpashop.repository.order.dto.OrderSimpleQueryDto] org.hibernate.hql.internal.ast.DetailedSemanticException: Unable to locate appropriate constructor on class [jpabook.jpashop.repository.order.dto.OrderSimpleQueryDto]. Expected arguments are: long, java.lang.String, java.time.LocalDateTime, jpabook.jpashop.domain.OrderStatus, jpabook.jpashop.domain.Address at org.hibernate.hql.internal.ast.tree.ConstructorNode.resolveConstructor(ConstructorNode.java:182) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final] at org.hibernate.hql.internal.ast.tree.ConstructorNode.prepare(ConstructorNode.java:144) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final] at org.hibernate.hql.internal.ast.HqlSqlWalker.processConstructor(HqlSqlWalker.java:1258) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final] at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectExpr(HqlSqlBaseWalker.java:2390) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final] at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectExprList(HqlSqlBaseWalker.java:2256) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final] at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectClause(HqlSqlBaseWalker.java:1518) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final] at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:597) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final] at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:325) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final] at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:273) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final] at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:276) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final] at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:192) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final] at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:144) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final] at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:113) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final] at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:73) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final] at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:162) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final] at org.hibernate.internal.AbstractSharedSessionContract.getQueryPlan(AbstractSharedSessionContract.java:604) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final] at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:716) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final] at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:816) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final] at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:113) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na] at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na] at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:362) ~[spring-orm-5.3.2.jar:5.3.2] at com.sun.proxy.$Proxy104.createQuery(Unknown Source) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na] at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na] at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:311) ~[spring-orm-5.3.2.jar:5.3.2] at com.sun.proxy.$Proxy104.createQuery(Unknown Source) ~[na:na] at jpabook.jpashop.repository.order.simplequery.OrderSimpleQueryRepository.findOrderDtos(OrderSimpleQueryRepository.java:16) ~[classes/:na] at jpabook.jpashop.repository.order.simplequery.OrderSimpleQueryRepository$$FastClassBySpringCGLIB$$1df97ea7.invoke(<generated>) ~[classes/:na] 소스 트리, 내용 첨부하겠습니다 ㅠㅠ
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
orderList에서 전체 상품을 조회하도록 변경하는 것에서 질문 있습니다.
OrderApiController에서 V5처럼 map을 통해서 in쿼리로 나가게끔해서 객체내부에 list가 있도록 만들었습니다. list는 orderItemQueryDto를 재사용해서 만들었습니다. @Datapublic class OrderDto { private Long orderId; private String name; private LocalDateTime orderDate; private OrderStatus orderStatus; private List<OrderItemQueryDto> orderItems; public OrderDto(Long orderId, String name, LocalDateTime orderDate, OrderStatus orderStatus) { this.orderId = orderId; this.name = name; this.orderDate = orderDate; this.orderStatus = orderStatus; }} list를 화면에 어떻게 뿌릴까하다가 each문을 이중으로 사용해서 만들긴 했는데 예상한데로 이상하게 나오더라고요 이렇게 list를 가지고 있는 객체에 대해서는 그냥 flat으로 만들어서 사용해야 하나요? List<Stream<OrderFlat>> orders = result.stream() .map(order -> { return order.getOrderItems().stream() .map(o -> { return new OrderFlat(order.getOrderId(), order.getName(), order.getOrderDate(), order.getOrderStatus(), o.getItemName(), o.getOrderPrice(), o.getCount()); }); }).collect(toList()); 혹시나해서 이렇게 해봤는데..안되네요 제생각으로는 flat말고는 해결이 안될거 같은데 혹시 다른 방법이 있을까요?
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
트래픽이 많은 상황에 대해 질문입니다
안녕하세요. 여태 배운걸 토대로 트래픽이 많은 상황에 대해서 생각해보고 있는데요. 클라이언트에서 order를 생성/변경하는 요청이 계속 들어온다고 생각해봤습니다. 각각의 요청에 대해서 결국 insert, 또는 update SQL이 생성될텐데요. 1. 그러면 클라이언트 1, 2, 3,,, 1000 이렇게 1000명이 동시에 요청해서 insert 또는 update를 하면, 1000개의 SQL이 각각 생성돼서 실행된다고 이해했는데 맞나요? 2. 위의 상황에서 제가 이해한 대로 1000개의 SQL이 각각 생성돼서 실행된다고하면, DB로 가는 네트워크에 부하가 생길수도 있을 것 같은데요. 이런게 문제되지는 않나요?? 혹시 이런 상황에 대해서 따로 처리하는 방법이 있는지 궁금합니다.
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
fetch join 일관성
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요. jpa활용 1편에서 fetch join 일관성 관련해서 아직 확실하지 이해하지 못한 것 같아서 활용 2편을 듣다가 질문이 생겼고 다른 수강생분이 올린 질문 내용과 답변을 보다가 궁금한 것이 생겨 질문드렸습니다. https://www.inflearn.com/questions/15876 위 링크에 있는 답변을 읽어보니 fetch join의 대상은 on,where 등에서 필터링 조건을 사용하면 안되지만 일관성이 깨지지 않는다면 사용할 수 있다고 답변을 주신 것 같습니다. 코드 1 select m from Member m join fetch m.team t where t.name=:teamName 코드 2 select t from Team t join fetch t.members m where m.username =:username (질문) 또한 위 링크에서 코드 1은 객체의 상태와 db의 상태의 일관성이 깨지지 않고 코드 2는 일관성이 깨진다고 답변해주셨고 위 두개의 코드가 왜 일관성이 깨지고,깨지지 않는지 생각해보았고 제가 생각한 것이 맞는지 알고 싶어서 질문을 드렸습니다. 코드 1이 일관성이 깨지지 않는다고 생각한 이유 memberA - team1 memberB - team1 memberC - team2 로 DB에 데이터가 있다고 가정 조인 대상의 필터링을 제공하여 조회결과가 team1만 조회하게 되면 memberA = {team1} memberB = {team1} 와 같은 결과를 얻게 되고 memberA를 조회할 경우 team1이 조회되고 mebmerB가 조회할 경우 team1이 조회됨으로 코드1은 일관성이 깨지지 않는다고 생각했습니다. 코드 2가 일관성이 깨진다고 생각한 이유 team1 - memberA team1 - memberB team1 - memberC 로 DB에 데이터가 있다고 가정 조인 대상의 필터링을 제공하여 조회 결과가 memberA, memberC만 조회하게 되면 team1 = {memberA, memberC}와 같은 결과를 얻게 되고 team1을 조회할 경우 memberC가 조회되지 않아 코드2는 일관성이 깨진다고 생각했습니다.
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
프로젝트 진행 관련 궁금한 점
영한님 서포터즈님들 안녕하세요 저는 영한님 spring 기본, http, MVC 1편, JPA 기본, JPA 1 을 듣고,인원을 구해서 현재 FE2명, BE2명과 함께 프로젝트를 진행하고 있습니다. 처음에는 배운 것을 써먹어보자라는 의도로 기능을 우선시 하는 프로젝트를 만들었습니다. 그러다보니 어떤 정보를 조회하는데 select 쿼리가 10개가 나가고 아주 가관입니다,, 그래서 영한님 JPA 2를 다 듣고나서 현재는 제가 저질러놓은 코드들을 최적화로 하나하나 정화 중입니다,, 제가 현재 생각하고 있는 것은 다른 프로젝트를 하지 않고 하나의 프로젝트를 완벽하게 만들어보자라는 생각으로아래와 같이 작성해보았는데 이게 잘하고 있는지 궁금해서 여쭤보고 싶습니다,, 1. 모든 기능들을 하나하나 최적화(현재 EntityManager를 통해 JPQL을 일일이 작성 중) -----물론 fetch join 뿐만 아니라 필요없는 데이터(username만 필요한데 굳이 Dto를 전송)를 줄이면서 최적화 하고 있습니다2. Spring data JPA를 듣고 리팩토링3. Querydsl을 듣고 리팩토링 현재 1번은 진행중인데 쿼리가 줄어들 때마다 너무 행복합니다,, 영한님 저엉말로 재밌게 배우고 있습니다 감사합니다
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
OrderITemDto 클래스 생성 위치
안녕하세요 좋은 영상 감사합니다. 다름이 아니라 OrderItemDto 는 OrderDto 안에서 사용하는 클래스이므로 OrderItemDto(내부 클래스) 의 다시 내부 클래스로 생성하는 것이 맞지 않나요? 영상에서는 OrderDto 와 동등한 레벨로 생성하셔서 궁금증이 들었습니다. 감사합니다^^
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
쿼리 방식 선택 권장 순서
영한님 좋은 강의 항상 감사합니다. 쿼리 방식 선택 권장 순서의 4번재에서 스프링 JDBC Template을 사용한다고 하셨는데요. 그 전의 단계에서 myBatis와 같은 sql mapper는 왜 사용하지 않는것인가요? 아니면 JDBC Template에 의미상 myBatis도 포함한 것일까요? 관련해서 JDBC에 대하여 한가지만 더 여쭤본다면 JDBC란 Java Database Connection 이라고 알고 있는데요, 좀 더 광범위한 의미로 사용되기도 하는 걸까요? 예를 들어서 여러가지 툴에서(Google Data Studio 에서 DB를 연결할 때 등) DB연결 정보에 'JDBC URL' 이라고 표기되어 있는 것이 이해가 안되더라구요. Mysql 등 DB와 연관이 있는 것이지 Java 랑은 전혀 무관한 것 아닌가요. 미리 감사드립니다! 항상 건강하세요.
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
update 메소드가 void 인것과 관련된 질문 드립니다.
안녕하세요. update 메소드는 커맨드 쿼리 분리 원칙에 따라서 member 객체를 반환하지 말고 void로 반환값이 없게 하거나 id정도만 반환하라고 하셨습니다. 1. 커맨드가 수정이니 쿼리로 member를 찾지 말아야 한다고 하셨는데 findOne 메소드에서 어차피 member를 찾아야 하는 것 아닌가요? 2. update 메소드 외에도 사실 모든 메소드에서 void나 id만 반환해야 하는 것인가요? 다른 메소드도 등록, 수정, 삭제 등등 멤버를 조회하는 것이 커맨드가 아닌 것 같은데, 그렇다면 일반적으로 반환을 id만 해주는 것이 좋은가요? 감사합니다.
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
안녕하세요 영한님 강의를 벗어난 주제 이긴한대 궁금한게 생겨서 질문드립니다.
유저와 팔로우 팔로잉 Entity 를 구현하려고 하는데 이렇게 하는게 맞는건지 잘 모르겠어서 질문드립니다.. 조금이라도 힌트를 주신다면 정말 감사할것 같네요!!
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
BatchSize, Batch Fetch Size 옵션이 적용되지 않는 문제
안녕하세요 영한님 강의 잘 듣고 있습니다! 해당 강의에서 글로벌전략을 지연로딩 설정후 Order -(one to Many) - OrderItemList -(Many To One)- Item 연관관계의 엔티티에 BatchSize, Batch Fetch Size 옵션을 적용해 3계층 깊이를 가진 엔티티 그래프를 조회하면 총 쿼리가 계층별로 하나씩, 3개 날라간다고 이해를 했습니다. 그런데 jdk 11, 스프링 Data Jpa와 mysql 등 약간의 환경 변화후 같은 예제로 테스트해보니 1. order 쿼리 2. orderItemList의 N개 쿼리(강의에서는 총 2개) 3. 이후 in절로 묶인 item 쿼리가 위의 N개쿼리때문에 N개 총 5개의 쿼리가 날라가는 현상이 발생했습니다. 요약하면 첫 연관 엔티티 orderitem은 batchsize 옵션이 적용되지 않아 N개의 쿼리가 날라가고 이후에 배치 옵션이 적용되어 각각 in절로 묶인 item 엔티티 쿼리가 날라가고있습니다. 이것저것 조작하다가 서비스단에서 transaction 어노테이션을 제거하니 강의에서처럼 엔티티 그래프 계층별로 한개씩 쿼리가 3개로 바뀌어 날라가는데 이유를 잘 모르겠습니다. transaction(Readonly = true) 일때도 정상적으로 쿼리가 3개 날라가는거보면 transaction(Readonly = false) 상황에서만 발생하는 문제같은데 혹시 원인을 알수 있을까요? 해당 문제를 고민하는 과정을 블로그에 글로 작성했는데 참조해서 정확한 원인을 알려주시면 정말 감사하겠습니다. https://www.jiniaslog.co.kr/article/view?articleId=559 언제나 양질의 강의 제공해주셔서 정말 감사하고 새해복 많이 받으세요!
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
V4 질문있습니다!
안녕하세요 영한님 서포터즈님들 V4에서 OrderQueryRepository 에 있는 findOrders 메서드에 대한 질문입니다. lazy인데 어떻게 member와 delivery가 같이 조회되는지 이해가 되질 않습니다.. m.name을 알기위해 member 엔티티를 select 해야한다고 생각을 했습니다 혹시 어느 부분이랑 제가 헷갈리는걸까요? 해당 단원 qna에서 해당 질문과 비슷한 게 있었는데 fetch join에 대해서만 말씀해주셔서 궁금해서 여쭤보게됐습니다 감사합니다 안녕하세요!! 질문 드리고 답을 기다리는 동안에 계속 찾아보고 생각해봤는데아직 저 자신한테 납득이 안되서요,, 혹시 맞는지 여쭤보고 싶습니다 API 개발 고급 - 지연 로딩과 조회 성능 최적화 강의에서 V4를 보고왔습니다 제가 의심한 부분은 "일반적인 SQL을 사용할 대 처럼 원하는 값을 선택해서 조회"입니다. 첫 번째 쿼리에 대한 데이터는 이렇게 되어있습니다 지연로딩은 프록시 객체를 만들어서 해당 엔티티의 필드를 사용할 때 DB에서 엔티티를 조회하는 것인데, 바로 DTO를 조회하는 것은 JOIN된 DB 값에서 member의 이름, delivery address 을 바로 뽑아서 값을 가져오기 때문에 JPA에서 엔티티를 조회하지 않아도 값을 알 수 있으며, 다른 select 쿼리가 나가지 않게 되는걸까요?
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
"org.springframework.http.converter.HttpMessageConversionException: Type definition error 에러
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]안녕하세요 강사님, 강의를 진행하는 도중 에러가 발생하여 글 남깁니다. <postman> http://localhost:8080/api/v2/simple-orders <error 메시지> { "timestamp": "2022-01-25T07:53:01.075+00:00", "status": 500, "error": "Internal Server Error", "trace": "org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class jpabook1.jpashop1.api.OrderSimpleApiController$SimpleOrderDto]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class jpabook1.jpashop1.api.OrderSimpleApiController$SimpleOrderDto and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.ArrayList[0])\n\tat org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:460)\n\tat org.springframework.http.converter.AbstractGenericHttpMessageConverter.write(AbstractGenericHttpMessageConverter.java:104)\n\tat org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:290)\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:183)\n\tat org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:78)\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:135)\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1067)\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)\n\tat org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:655)\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:764)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:540)\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:382)\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:895)\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1732)\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\n\tat org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)\n\tat org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\n\tat java.base/java.lang.Thread.run(Thread.java:831)\nCaused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class jpabook1.jpashop1.api.OrderSimpleApiController$SimpleOrderDto and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.ArrayList[0])\n\tat com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77)\n\tat com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1300)\n\tat com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:400)\n\tat com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:46)\n\tat com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:29)\n\tat com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serializeContents(CollectionSerializer.java:145)\n\tat com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:107)\n\tat com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:25)\n\tat com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480)\n\tat com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:400)\n\tat com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1514)\n\tat com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:1007)\n\tat org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:454)\n\t... 48 more\n", "message": "Type definition error: [simple type, class jpabook1.jpashop1.api.OrderSimpleApiController$SimpleOrderDto]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class jpabook1.jpashop1.api.OrderSimpleApiController$SimpleOrderDto and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.ArrayList[0])", "path": "/api/v2/simple-orders" } <OrderSimpleApiController.java> @RestController@RequiredArgsConstructorpublic class OrderSimpleApiController { private final OrderRepository orderRepository; /** * 주문 조회 */ @GetMapping("/api/v2/simple-orders") public List<SimpleOrderDto> ordersV2() { //Order 테이블 조회 (결과 2개) List<Order> orders = orderRepository.findAllByCriteria(new OrderSearch()); List<SimpleOrderDto> result = orders.stream() .map(o -> new SimpleOrderDto(o)) .collect(Collectors.toList()); return result; } //DTO로 변환 static class SimpleOrderDto { private Long orderId; private String name; private LocalDateTime orderDate; private OrderStatus orderStatus; private Address address; //생성자 public SimpleOrderDto(Order order) { orderId = order.getId(); name = order.getMember().getName(); //Member 테이블 조회 orderDate = order.getOrderDate(); orderStatus = order.getStatus(); address = order.getMember().getAddress(); //Delivery 테이블 조회 } } //fetch join으로 쿼리 1번 호출 @GetMapping("/api/v3/simple-orders") public List<SimpleOrderDto> orderV3() { List<Order> orders = orderRepository.findAllWithMemberDelivery(); List<SimpleOrderDto> result = orders.stream() .map(o -> new SimpleOrderDto(o)) .collect(Collectors.toList()); return result; }} <Order.java> @Entity@Table(name = "orders")@Getter @Setterpublic class Order { @Id @GeneratedValue @Column(name = "order_id") private Long id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "member_id") private Member member; @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumn(name = "delivery_id") private Delivery delivery; @OneToMany(mappedBy = "order", cascade = CascadeType.ALL) private List<OrderItem> orderItems = new ArrayList<>(); private LocalDateTime orderDate; @Enumerated(EnumType.STRING) private OrderStatus status; //ORDER, CANCEL //연관관계 편의 메서드 public void setMember(Member member) { this.member = member; member.getOrders().add(this); } public void addOrderItem(OrderItem orderItem) { this.orderItems.add(orderItem); orderItem.setOrder(this); } public void setDelivery(Delivery delivery) { this.delivery = delivery; delivery.setOrder(this); } //생성 메서드 public static Order createOrder(Member member, Delivery delivery, OrderItem... orderItems) { Order order = new Order(); order.setMember(member); order.setDelivery(delivery); for(OrderItem orderItem : orderItems) { order.addOrderItem(orderItem); } order.setOrderDate(LocalDateTime.now()); order.setStatus(OrderStatus.ORDER); return order; } //비즈니스 로직 (주문 취소) public void cancel() { if(delivery.getStatus() == DeliveryStatus.COMP) { throw new IllegalStateException("이미 배송완료된 상품은 취소가 불가능합니다."); } this.setStatus(OrderStatus.CANCEL); for(OrderItem orderItem : orderItems) { orderItem.cancel(); //Item의 stockQuantity를 늘리기 위해 orderItem을 이용 (orderItem과 Item이 연관관계니깐) } } //조회 로직(전체 주문 가격) public int getTotalPrice() { int totalPrice = 0; for(OrderItem orderItem : orderItems) { totalPrice += orderItem.getTotalPrice(); } return totalPrice; } protected Order() {}} <Delivery.java> @Entity@Getter @Setterpublic class Delivery { @Id @GeneratedValue private Long id; @OneToOne(mappedBy = "delivery", fetch = FetchType.LAZY) @JsonIgnore private Order order; @Embedded private Address address; @Enumerated(EnumType.STRING) private DeliveryStatus status; //READY, COMP} <Member.java> @Entity@Getter @Setterpublic class Member { @Id @GeneratedValue @Column(name = "member_id") private Long id; private String name; @Embedded private Address address; @JsonIgnore @OneToMany(mappedBy = "member") //Order클래스의 member 필드에 의해 mapped by 되는거야! private List<Order> orders = new ArrayList<>();} <OrderItem.java> @Entity@Getter @Setterpublic class OrderItem { @Id @GeneratedValue @Column(name = "order_item_id") private Long id; @JsonIgnore @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "order_id") private Order order; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "item_id") private Item item; private int orderPrice; private int count; //생성 메서드 public static OrderItem createOrderItem(Item item, int orderPrice, int count) { OrderItem orderItem = new OrderItem(); orderItem.setItem(item); orderItem.setOrderPrice(orderPrice); orderItem.setCount(count); item.removeStock(count); return orderItem; } //비즈니스 로직(주문취소) public void cancel() { item.addStock(count); } //조회 로직(전체 주문 가격) public int getTotalPrice() { return orderPrice * count; } protected OrderItem() {}} 양방향 연관관계가 걸린 한쪽에 @JsonIgnore을 작성하였지만 에러가 계속 발생하여 문의 드립니다. 항상 강의 잘 듣고 있습니다. 감사합니다.
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
최적화 의미
강사님, 서포터즈 님들 안녕하세요 오랜만에 질문 남기는 것 같습니다 JPA 응용 1까지 듣고 팀원 모아서 플젝을 하면서 전체적인 기능 틀은 완성했습니다. 이제 리팩토링을 한 뒤, 기능을 천천히 추가해보려 하는데 그 전에최적화를 신경쓰지 않았더니 기능 하나당 나가는 쿼리 개수가 이만저만이 아니였습니다 그래서 최적화 하려고 강의 들으러 왔습니다! 제가 궁금한 점은 최적화라 함은 날라가는 쿼리의 개수를 줄이는 것 또는 기능 하나가 완료될 때까지 걸리는 시간을 줄이는 것 이라고 생각합니다. 그러면 각 기능을 구현한 메서드 하나당 시작할 때와 완료될 때까지의 시간을 항상 계산하는건가요? ex) System.currentTimeMills()를 이용하여 시작과 끝에서 구한 뒤 뺀 값으로 시간을 측정
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
JPA selfjoin
[질문 템플릿]1. 강의 내용과 관련된 질문인가요?실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]오라클에 있는 self 조인은 dsl문으로 불가능한가여? 예시 TABLE A PK 데이터1, 데이터2 TABLE B PK FK1 FK2 연관관계를 조회하고싶습니다. TABLE A에 있는 PK 데이터 1, 데이터2 들이 서로 연관되어있다는걸 예시입니다. TABLE A -> TABLE B -> TABLE A
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
generated 된 QModel 들을 못 불러 옵니다.
강의영상과 자료에 의거해서 환경을 세팅하고 Tasks > other > compileQuerydsl 까지 실행해서 해당 패키지에 파일을 생성하는 것 까지는 성공했는데... path 설정의 문제인지 불러올 수가 없습니다. --------------------------------------------------- //querydsl 추가buildscript { dependencies { classpath("gradle.plugin.com.ewerk.gradle.plugins:querydsl-plugin:1.0.10") }}plugins { id 'org.springframework.boot' version '2.6.2' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java'}apply plugin: "com.ewerk.gradle.plugins.querydsl"group = 'jpabook'version = '0.0.1-SNAPSHOT'sourceCompatibility = '11'configurations { compileOnly { extendsFrom annotationProcessor }}repositories { mavenCentral()}dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-devtools' implementation("com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.8.0") implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'com.fasterxml.jackson.datatype:jackson-datatype-hibernate5' implementation 'junit:junit:4.13.1' compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.h2database:h2' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' //querydsl 추가 implementation 'com.querydsl:querydsl-jpa' //querydsl 추가 implementation 'com.querydsl:querydsl-apt'}//querydsl 추가def querydslDir = 'src/main/generated'//def querydslDir = "$buildDir/generated/querydsl"querydsl { library = "com.querydsl:querydsl-apt" jpa = true querydslSourcesDir = querydslDir}sourceSets { main { java { srcDirs = ['src/main/java', querydslDir] } }}compileQuerydsl { options.annotationProcessorPath = configurations.querydsl}configurations { querydsl.extendsFrom compileClasspath}test { useJUnitPlatform()} ---------------------------------------------------
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
현재 상황에서 OrderItem에 대한 조회가 왜 일어나는 지 잘 모르겠습니다.
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 네2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 네3. 질문 잘하기 메뉴얼을 읽어보셨나요?네[질문 내용] 안녕하세요 영한 선생님!! 강의 잘 보던 중 의문이 생겨 질문이 드립니다. /api/v3.1/orders를 만드는 과정에서 (batch size 적용) batch size 적용 후 OrderItem에 대해 쿼리가 나가는 시점(Lazy가 적용이 되는 것인지)을 명확하게 모르겠어서 도메인을 DTO로 변환하지 않고 직접 도메인을 반환하도록 약간의 수정을 해봤는데요! api 코드입니다. @GetMapping("/api/v3.1/orders") public List<Order> ordersV3_page( @RequestParam(value="offset", defaultValue = "0") int offset, @RequestParam(value="limit", defaultValue = "100") int limit ) { List<Order> orders = orderRepository.findAllWithMemberDelivery(offset, limit);// List<OrderDto> collect = orders.stream()// .map(o -> new OrderDto(o))// .collect(Collectors.toList()); return orders; } Repository 코드입니다. public List<Order> findAllWithMemberDelivery(int offset, int limit) { return em.createQuery( "select o from Order o" + " join fetch o.member m" + " join fetch o.delivery d", Order.class) .setFirstResult(offset) .setMaxResults(limit) .getResultList();} /api/v3.1/orders 조회 쿼리입니다. 조회 response입니다. [ { "id": 4, "member": { "id": 1, "name": "userA", "address": { "city": "서울", "street": "1", "zipcode": "1111" } }, "orderItems": null, "delivery": { "id": 5, "address": { "city": "서울", "street": "1", "zipcode": "1111" }, "status": null }, "orderDate": "2022-01-19T20:14:36.130898", "status": "ORDER", "totalPrice": 30000 }, { "id": 11, "member": { "id": 8, "name": "userB", "address": { "city": "진주", "street": "2", "zipcode": "2211" } }, "orderItems": [ { "id": 13, "item": null, "orderPrice": 20000, "count": 2, "totalPrice": 40000 }, { "id": 14, "item": null, "orderPrice": 80000, "count": 4, "totalPrice": 320000 } ], "delivery": { "id": 12, "address": { "city": "진주", "street": "2", "zipcode": "2211" }, "status": null }, "orderDate": "2022-01-19T20:14:36.161899", "status": "ORDER", "totalPrice": 360000 } ] 그럼 의문 사항인데요, 도메인을 직접적으로 반환하는 과정에서 1. OrderItem에 직접적으로 접근하지 않았고, Order entity에 정의된 OrderItem은 Lazy Loading인데 왜 OrderItem에 대한 조회 쿼리가 발생했는지 잘 모르겠습니다. 2. OrderItem에 대한 조회 쿼리가 발생했음에도 불구하고 왜 Order Id 11은 OrderItem이 조회가 되고 Order Id 4에 대한 OrderItem은 여전히 Null로 조회되는지 잘 모르겠습니다. 소스 코드 링크입니다. (문제 상황이 도출되도록 수정이 되어있습니다.) https://drive.google.com/file/d/1cLs3su4q1R2iIJPAe3K9pral-SqhMKk9/view?usp=sharing 포스트맨을 통해 /api/v3.1/orders 를 실행했을 때 일어나는 현상입니다. 답변주시면 감사하겠습니다!!! 강의 내용이 너무 도움이 되고 있습니다.