30%
61,600원
다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
Hibernate5Module 관련 질문
Hibernate5Module 애가 빈으로 등록되어 있기 때문에아래 코드에서 프록시 객체인 상태를 getUsername()로 프록시 초기화 시켜 바로 api로 반환 가능한건가요? List<Order> all = orderRepository.findAllByCriteria(new OrderSearch()); for (Order order : all) { order.getMember().getUsername(); } return all; }
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
중복 메서드 추출 안되네요 ㅠㅠ
메서드 추출은 되는데IntelliJ IDEA 2023.2.2 Ultimate(컴퓨터는 맥북)해당 버전 사용중입니다 중복된 메서드일때 intellij 에서Extract Parameters to Replace Duplicate(10:34 초부분)이건 안되네요.인텔리제이 버전 업하면서 그런거같은데혹시 해당 화면처럼 하는 팁 있을까요?package jpabook.jpashop; import jakarta.annotation.PostConstruct; import jakarta.persistence.EntityManager; import jpabook.jpashop.domain.*; import jpabook.jpashop.domain.item.Book; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; /** * 총 주문 2개 * userA * * JPA1 BOOK * * JPA2 BOOK * userB * * SPRING1 BOOK * * SPRING2 BOOK */ @Component @RequiredArgsConstructor public class initDb { private final InitService initService; @PostConstruct public void init() { initService.dbInit1(); // 여기에 코드들 다 넣는 경우는 작동하지 않는다 initService.dbInit2(); } @Component @Transactional @RequiredArgsConstructor static class InitService { private final EntityManager em; public void dbInit1() { Member member = new Member(); member.setName("userA"); member.setAddress(new Address("서울", "1", "1111")); em.persist(member); Book book1 = new Book(); book1.setName("JPA1 BOOK"); book1.setPrice(10000); book1.setStockQuantity(100); em.persist(book1); Book book2 = new Book(); book2.setName("JPA2 BOOK"); book2.setPrice(20000); book2.setStockQuantity(100); em.persist(book2); OrderItem orderItem1 = OrderItem.createOrderItem(book1, 10000, 1); OrderItem orderItem2 = OrderItem.createOrderItem(book1, 20000, 2); Delivery delivery = new Delivery(); delivery.setAddress(member.getAddress()); Order order = Order.createOrder(member, delivery, orderItem1, orderItem2);// ... 되어있으면 여러개 넘길 수 있다 em.persist(order); } public void dbInit2() { Member member = new Member(); member.setName("userB"); member.setAddress(new Address("진주", "2", "2222")); em.persist(member); Book book1 = new Book(); book1.setName("JPA1 BOOK"); book1.setPrice(10000); book1.setStockQuantity(100); em.persist(book1); Book book2 = new Book(); book2.setName("JPA2 BOOK"); book2.setPrice(20000); book2.setStockQuantity(100); em.persist(book2); OrderItem orderItem1 = OrderItem.createOrderItem(book1, 10000, 1); OrderItem orderItem2 = OrderItem.createOrderItem(book1, 20000, 2); Delivery delivery = new Delivery(); delivery.setAddress(member.getAddress()); Order order = Order.createOrder(member, delivery, orderItem1, orderItem2);// ... 되어있으면 여러개 넘길 수 있다 em.persist(order); } } }전체코드고 아래 부분을 메서드 추출하면 중복 체크하고 매개변수 넘기는 방식으로는 안되네요.. Member member = new Member(); member.setName("userA"); member.setAddress(new Address("서울", "1", "1111")); em.persist(member);
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
처음 저장한 데이터가 조회가 안됩니다.
안녕하세요. 현재 대학생이고 김영한님 강의를 본 후 프로젝트를 진행중입니다.다름이 아니라, 현재 Tale과 Keyword의 다대다 연관관계를 피하기 위해 중간에 TaleKeyword라는 중간 테이블을 두었고, Tale은 Member 테이블과 일대일 연관관계를 설정해두었습니다.동화(Tale)를 키워드, 회원정보에 끼워넣고 생성하는 API는 완성하였고 DB에 쌓이는 것까지 확인했습니다. 하지만, memberId를 이용해서 조회하려고 하니, 맨 처음 memberId가 1인 회원의 처음 저장한 taleId 1번의 데이터와 memberId가 2인 회원의 처음 저장한 동화인 taleId가 4인 동화가 조회가 안됩니다.(나머지 taleId 2, 3, 5번 데이터는 조회가 잘됩니다.) 쿼리문은 다음과 같이 짰고 영속성 문제인 것 같은데, 왜 첫번째 데이터만 조회가 안되는 걸까요?// 동화 목록조회(페이징) public List<Tale> findTalesByMemberId(Long memberId, int offset, int limit) { return em.createQuery( "select t from Tale t" + " join fetch t.member m" + " join fetch t.image.taleImage ti" + " where m.id = :memberId" + " order by t.createdTime desc", Tale.class ) .setParameter("memberId", memberId) .setFirstResult(offset) .setMaxResults(limit) .getResultList(); }
- 해결됨실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
Lazy 로딩 , FetchJoin 그리고 @BatchSize
=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]공부를 하면서 제가 생각하는 부분이 맞는지 확인차 질문 드립니다. 모든 XToOne 은 fetch:Lazy로 되어있다는 가정Member와 Order는 양방향 참조Member를 사용할 때 Order는 가끔 사용되는경우Lazy 로 하고 Order를 사용할 때 마다 쿼리 나감자주 사용되는 경우패치조인을 사용해서 한번에 같이 불러온다.컬렉션인 경우XToOne은 패치 조인 하고 @BatchSize를 사용해서 페이징 및 최적화 까지 챙긴다 제 생각으론 동작방법만 제대로 알고 있으면 실무에서는default_batch_fetch_size 는 계속 등록해서 글로벌로 사용하면 좋아보이는데 그게 맞나요 ?
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
default_batch_fetch_size: 100 으로 설정을 해줘도 쿼리가 한번에 가져오지 않습니다.
@GetMapping("api/v2/orders/{id}") // batch_fetch_size 검색용 public ResultMany findByBatchFetch(@PathVariable("id") Long id) { Customer customer = customerService.findCustomerById(id); List<Order> orders = orderRepository.findAllOrder(); return getOrderDtoList(orders); } public List<Order> findAllOrder() { return em.createQuery("select o from Order o", Order.class) .getResultList(); } private ResultMany getOrderDtoList(List<Order> orders) { List<OrderDto> orderDtos = orders.stream() .map(o -> new OrderDto(o)) .collect(Collectors.toList()); return new ResultMany<>(orders.size(), orderDtos); } @Data static class OrderDto { private Long id; private CustomerDto customer; private List<DiffuserProductRequestDto> diffuserProductRequest; public OrderDto (Order order) { id = order.getId(); customer = new CustomerDto(order.getCustomer()); diffuserProductRequest = order.getDiffuserProductRequests().stream() .map(diff -> new DiffuserProductRequestDto(diff)) .collect(Collectors.toList()); } } @Data static class DiffuserProductRequestDto { private Long id; private DiffuserDto diffuser; private int amount; private Deadline deadline; private ProductionStatus status; public DiffuserProductRequestDto (DiffuserProductRequest diffuserProductRequest) { id = diffuserProductRequest.getId(); diffuser = new DiffuserDto(diffuserProductRequest.getDiffuser()); amount = diffuserProductRequest.getAmount(); deadline = diffuserProductRequest.getDeadline(); status = diffuserProductRequest.getStatus(); } } 위에는 order클래스 연관된 클래스를 찾기위한 코드들인데 제가 27개의 오더를 만들고 get요청을 보내면 default_batch_fetch_size: 100으로 설정 해놓았기 때문에 27개를 한번에 가져올 것이라고 생각하고 있는데 결과는 그렇지 않습니다이렇게 두번의 쿼리로 찾아오는데 어떤게 문제인걸까요?
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
OSIV OFF시 Sercice클래스 최적화
orderService.class를 예를들면 Order Entity를 저장하거나 find하거나 등 entity위주의 코드만 뒀습니다.허나 Order Entity뿐만 아니라 OrderForm, OrderDto를 변환해주는 코드가 필요하다면 각 형태를 변화해주는 Service를 따로 만들어야하는지 아님 하나의 Service클래스안에 다 둬야 하는지 궁금합니다.예로들어 OrderEntity => OrderForm 로바꿔주는서비스, OrderEntity => OrderDto로 바꿔주는 서비스,OrderEntity를 repository에 넘겨주는(DB에 저장하는) 서비스 이런식으로 각각 나눠서 여러개의 클래스로 만들어주나요??
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
v2에서 No serializer found for class ~ 문제가 발생합니다.
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요.DTO로 변환을 해줬는데 왜 이러한 문제점이 발생하는지 잘 모르겠습니다. 감사합니다. [OrderDto][log]소스압축파일https://drive.google.com/file/d/1Kb9yLRF3-AkxyBlvC8Aum_3gumKI21Ol/view?usp=sharing
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
SimpleOrderDto에서의 Address
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요.SimpleOrderDto에서의 Address를 왜 (order.getDelivery().getAddress()) 오더의 딜리버리에서 가져오는 건가요?? 오더에 멤버에서 가져올수도 있는데 딜리버리에서 가져오는 이유가 있을까요? 멤버에서 가져오면 쿼리가 3번이면 끝나고 딜리버리에서 가져오면 5번의 쿼리를 거쳐서 조금 헷갈립니다
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
@oneToMany의 @oneToMany
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니요) 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) 예EntityA 1:N EntityBEntityB 1:N EntityC이런식으로 있을 때 ,EntityA 가 EntityB를 @oneToMany로 가지고있고,EntityB 가 EntityC를 @oneToMany로 가지고 있습니다. 배치사이즈 옵션을 넣고select a from EntityA a 했을때 , EntityA의 갯수만큼 EntityB에 in절로 EntityA의 키값을 넣어서 @oneToMany를 한번에 끌고왔다고 했을 때 ,EntityB안에있는 EntityC를 또 EntityB의 갯수만큼 EntityC에 in절로 EntityB의 키값을 넣어서 한번에 끌고 올 수 있는 방법이 있는지 궁금했습니다. 다중 페치조인 안되는걸 알지만 예를 들어서 이런걸 원한다고 생각하시면 될 것 같습니다select a from EntityA a join fetch EnitityB(List) b join fetch b.EntityC(List)이런식으로 컬럼 갯수가 일대다 일대다로 기하급수적으로 증가하는 경우인데배치사이즈를 사용하여 select a from EntityA a 를 했을 땐 a.EntityB 에 접근할때 당연히 in 절로 가져와주지만 EntityB의 일대다인 EntityC 를 조회 할 땐 N+1이 다시 터집니다엮여있는 모든 연관관계들을 IN절로 태우고 싶은데 방법이 없을까요?
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
v6에서 groupingBy가 아닌 distinct
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]안녕하세요! [강의] 주문 조회 V6: JPA에서 DTO로 직접 조회, 플랫 데이터 최적화 관련해서 궁금한 점이 있습니다. findAllByDto_flat()에서 distinct를 통해 중복을 제거하는게 아니라 강의와 같이 findAllByDto_flat() 호출 후 groupingBy를 통해 중복을 제거했을 때 서로 어떤 차이가 있나요?
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
수업 자료 코드 오타
강의에서는 List<Order> orders = orderRepository.findAllByString(new OrderSearch()); 로 수업해주시는데 강의자료에는 List<Order> orders = orderRepository.findAll(); 로 나와있습니다.
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
6:40 샘플데이터 EntityManager 인식문제.
트랜잭션안에 쓸수있는 EntityManager 없다고 에러가 뜹니다. 설정 문제인걸까요?org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'initDb': Invocation of init method failed; nested exception is javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'persist' call at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:160) ~[spring-beans-5.3.29.jar:5.3.29] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:440) ~[spring-beans-5.3.29.jar:5.3.29] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1796) ~[spring-beans-5.3.29.jar:5.3.29] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620) ~[spring-beans-5.3.29.jar:5.3.29] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.29.jar:5.3.29] at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.29.jar:5.3.29] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.29.jar:5.3.29] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.29.jar:5.3.29] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.29.jar:5.3.29] at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955) ~[spring-beans-5.3.29.jar:5.3.29] at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:921) ~[spring-context-5.3.29.jar:5.3.29] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.29.jar:5.3.29] at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.14.jar:2.7.14] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:731) ~[spring-boot-2.7.14.jar:2.7.14] at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) ~[spring-boot-2.7.14.jar:2.7.14] at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) ~[spring-boot-2.7.14.jar:2.7.14] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1303) ~[spring-boot-2.7.14.jar:2.7.14] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1292) ~[spring-boot-2.7.14.jar:2.7.14] at jpabook.jpashop.JpashopApplication.main(JpashopApplication.java:10) ~[main/:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[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:568) ~[na:na] at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) ~[spring-boot-devtools-2.7.14.jar:2.7.14] Caused by: javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'persist' call at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:299) ~[spring-orm-5.3.29.jar:5.3.29] at jdk.proxy3/jdk.proxy3.$Proxy119.persist(Unknown Source) ~[na:na] at jpabook.jpashop.InitDb$InitService.dbInit1(InitDb.java:34) ~[main/:na] at jpabook.jpashop.InitDb.init(InitDb.java:20) ~[main/:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[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:568) ~[na:na] at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:389) ~[spring-beans-5.3.29.jar:5.3.29] at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:333) ~[spring-beans-5.3.29.jar:5.3.29] at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:157) ~[spring-beans-5.3.29.jar:5.3.29] ... 23 common frames omitted 제코드입니다. package jpabook.jpashop; import jpabook.jpashop.domain.*; import jpabook.jpashop.domain.item.Book; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import javax.annotation.PostConstruct; import javax.persistence.EntityManager; @Component @RequiredArgsConstructor public class InitDb { private final InitService initService; @PostConstruct public void init() { initService.dbInit1(); } @Component @Transactional @RequiredArgsConstructor static class InitService { private final EntityManager em; private void dbInit1() { Member member = new Member(); member.setName("UserA"); member.setAddress(new Address("서울", "1", "1111")); em.persist(member); Book book1 = new Book(); book1.setName("JPA1 BOOK"); book1.setPrice(10000); book1.setStockQuantity(100); em.persist(book1); Book book2 = new Book(); book2.setName("JPA2 BOOK"); book2.setPrice(20000); book2.setStockQuantity(100); em.persist(book2); OrderItem orderItem1 = OrderItem.createOrderItem(book1, 10000, 1); OrderItem orderItem2 = OrderItem.createOrderItem(book2, 20000, 1); Delivery delivery = new Delivery(); delivery.setAddress(member.getAddress()); Order order = Order.createOrder(member, delivery, orderItem1, orderItem2); em.persist(order); } } }
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
재질문 드립니다 죄송합니다
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요. EntityA 1:N EntityBEntityB 1:N EntityC이런식으로 있을 때 ,EntityA 가 EntityB를 @oneToMany로 가지고있고,EntityB 가 EntityC를 @oneToMany로 가지고 있습니다. 배치사이즈 옵션을 넣고select a from EntityA a 했을때 , EntityA의 갯수만큼 EntityB에 in절로 EntityA의 키값을 넣어서 @oneToMany를 한번에 끌고왔다고 했을 때 ,EntityB안에있는 EntityC를 또 EntityB의 갯수만큼 EntityC에 in절로 EntityB의 키값을 넣어서 한번에 끌고 올 수 있는 방법이 있는지 궁금했습니다. 다중 페치조인 안되는걸 알지만 예를 들어서 이런걸 원한다고 생각하시면 될 것 같습니다select a from EntityA a join fetch EnitityB(List) b join fetch b.EntityC(List)이런식으로 컬럼 갯수가 일대다 일대다로 기하급수적으로 증가하는 경우인데배치사이즈를 사용하여 select a from EntityA a 를 했을 땐 a.EntityB 에 접근할때 당연히 in 절로 가져와주지만 EntityB의 일대다인 EntityC 를 조회 할 땐 N+1이 다시 터집니다엮여있는 모든 연관관계들을 IN절로 태우고 싶은데 방법이 없을까요?
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
확실한 답을 못얻었습니다.
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요. 일단 페치조인으로 둘이상의 컬렉션은 페치 조인 할 수 없다는 걸 알았습니다, 이럴땐 배치사이즈로 컬렉션들을 in 절로 끌고오면 되는것도 이해했습니다. 궁금한것은 in절로 끌고온 컬렉션들안에 또 @oneToMany로 선언된 엔티티가 존재한다면 그 컬렉션들은 @Batchsize옵션으로 in절로 끌고 올 수 가 없나요? 배치 사이즈를 줘도 컬조회할 기준 엔티티에 직접적으로 존재하지 않는 컬렉션 엔티티들은 in절로 끌고오지않고 n+1이 터지는것같습니다
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
restApi, html api 스펙
restApi를 배울땐 api스펙이 달라져서 데이터를 보낼때와 받을때 Dto로 바꿔서 했는데html에서 데이터를 보낼때는 Dto로 만들지 않고 entity로 데이터를 주고 받는데 html에서도 dto로 바꿔서 해야하는 거 아닌가요???
- 해결됨실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
API 호출 방법
안녕하세요. JPA 활용 강의를 들으며 추가로 REST API에 대해 공부중입니다.@Controller에서 페이지 렌더링을 하고,@RestController에서 API를 작성하는 것은 이해했습니다. 그러나 model로만 데이터를 넘기다가 API에 대해 공부를하다보니 호출 방식에 대해 이해가 되지 않습니다. 만약에 회원조회 기능을 만든다고 가정하면[1]@RestController에서 /api/members에서 회원 조회하여 ResponseEntity<MemberDto>를 리턴해주고,@Controller에서 /api/members를 호출한 다음에 MemberDto로 변경하여 Model에 담은 후 /memberList로 렌더링 [2]@Controller에서 /memberList로 렌더링한 후, javascript에서 fetch()를 사용하여 /api/members를 호출하여 가져온 값을 화면에 뿌려줌 제가 고민해본 방식은 위 2가지입니다... 올바른 방식이 있는지,, 없다면 어떤 방식이 맞는 지 조언 해주시면 감사하겠습니다... ========================================앗 해당 질문글을 작성한 후 더 공부를 해보았는데요,CSR (클라이언트 사이드 렌더링)와 SSR (서버 사이드 렌더링) 방식으로 두 가지 방식 모두 존재하는 방식이었군요... (제가 고민해본 방식과 CSR, SSR가 동일한 방식인지는 모르겠습니다... 맞나요...?) 페이지 이동과 동시에 조회가 필요한 곳인 경우 (필터 없이 회원조회) = 페이지 렌더링할때 api를 호출하여 model에 담아 넘기고, 필터로 조회하는 경우 = API만 호출하여 데이터 보여주기 위에 내용으로 개발하면 될까요?? (저는 현재 백엔드 개발자를 준비하며 혼자서 포트폴리오용 프로젝트를 만들고있습니다!)
- 해결됨실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
JPA 최적화 강의 수강 후, 개인 프로젝트 수행하면서 생긴 질문입니다.
안녕하세요. JPA 최적화 강의를 수강하고, 개인적으로 DDD를 책으로 공부해본 후에 개인 토이 프로젝트를 진행하던 중, 뜻밖에 의문이 생겼는데 여쭐 곳이 없어서 이렇게 질문 올리게 되었습니다. 맨 땅에 헤딩으로 이런저런 강의, 책, 다른 분들의 소스를 참고 하려다보니 여러 개념이 뒤섞여서 혼동이 옵니다.. ㅠㅠ 현재 프로젝트에서는 크게 에그리거트를 CUSTOMER, EXTERNAL, SECURITYMEDIA 3개로 나누어 설계했는데요. 강의에서도, 책에서도 DOMAIN 계층에 있는 서비스는 해당 도메인에 대한 순수한 CRUD를 수행하는 것으로 보았습니다. DDD 책에서는 여러 에그리거트가 필요로 하는 기능을 구현할 때는 도메인 서비스로 구현하라고 이야기 했구요.처음에는 책에서 조언하는 대로 도메인 서비스로 구현해보고자 하다가, 좀처럼 구현이 안되어서 다른 분들이 구현한 소스를 참조하다 보니 application(응용)영역을 FACADE라는 상위 계층을 두는 것을 방식을 알게 되었습니다. 소스를 따라가보니 각 애그리거트의 DOMAIN 영역에 있는 서비스를 주입하여, 각 도메인 영역에 있는 서비스를 적절히 호출하기에 책에서 본 도메인 서비스와 같은 역할을 하겠구나 하여,, 해당 프로젝트 구성 방식을 따라 개발해보기로 했습니다.그런데 개발을 하다보니,, 참조하는 소스에서 메소드 단위의 트랜잭션의 적용을 facade 영역이 아닌, 도메인 영역의 서비스 구현체에서 하는 것을 알게 되었습니다. 제가 개발하고자 기능은 여러 애그리거트를 생성, 변경하는 하나의 행위가 하나의 트랜잭션으로 묶여야 하는데 말이죠.이러한 이유 때문에 현재 소스는 FACADE에서는 하나의 도메인 영역의 서비스를 주입하여 하나의 메소드를 호출하도록 되어있고, 도메인 영역에 있는 해당 서비스의 구현체에서 여러 애그리거트의 서비스, 레포지토리를 주입받아 하나의 메소드에서 트랜잭션 단위로 수행하도록 구현되어있습니다..@Service @RequiredArgsConstructor public class SecurityMediaFacade { private final SecurityMediaService securityMediaService; public SecurityMediaInfo.Main registerOtp(SecurityMediaCommand.RegisterSecurityMediaRequest req) { //디지털otp 발급 // 디지털 otp 발행 return securityMediaService.issueSecurityMedia(req, SecurityMediaType.DIGITAL_OTP); } ... }public interface SecurityMediaService { public SecurityMediaInfo.Main issueSecurityMedia(SecurityMediaCommand.RegisterSecurityMediaRequest req, SecurityMediaType type); ... }@Slf4j @Service @RequiredArgsConstructor public class SecurityMediaServiceImpl implements SecurityMediaService { private final CustomerReader customerReader; private final SecurityMediaStore securityMediaStore; private final TokenStore tokenStore; private final ExternalClientService externalClientService; @Override @Transactional public SecurityMediaInfo.Main issueSecurityMedia(SecurityMediaCommand.RegisterSecurityMediaRequest req, SecurityMediaType type) { // 요청고객 찾기 Customer customer = customerReader.findCustomerByRnn(req.getRnn()); SecurityMedia newOtp = null; if(!customer.existActiveSecurityMedia()) { // otp 신규 SecurityMedia initOtp = req.toEntity(SecurityMediaType.DIGITAL_OTP, customer); newOtp = securityMediaStore.store(initOtp); // 토큰 발급 요청 Token newToken = externalClientService.getToken(customer, newOtp); newOtp.addToken(newToken); tokenStore.store(newToken); } return new SecurityMediaInfo.Main(newOtp); }위에는 프로젝트의 구성인데.. 첫 단추부터 잘못 끼운 것도 같아서 시작 단계인 지금에서라도 좀 개선을 해보려고 하는데요.사실 도메인 서비스가 제가 의도로 하는 여러 애그리거트의 서비스 기능을 묶어서 하는 건지 아무리 읽어봐도 혼선이 옵니다. 혹시 DDD 책에서 이벤트라는 개념이 나오는데 도메인 서비스가 아니라, 이 이벤트를 통해 다른 애그리거트의 응용 서비스를 호출하도록 핸들링 하는게 올바른 방법일까요?지금과 같은 구조를 유지해도 된다면.. facade 영역의 메소드를 트랜잭션으로 묶고, 각 도메인 계층의 서비스들에서 선언된 해당 도메인에 대한 crud 메소드를 적절히 호출해가면서 facade 영역에서 비즈니스 로직을 처리해도 될까요? 너무 글이 장황하고 기네요.. ㅠㅠ 혹시 도움을 주신다면 너무나도 감사드리겠습니다.
- 미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
엔티티 그래프를 사용하지 않는 이유가 있나요?
연관 데이터를 불러와야 하는 상황에서 지연 로딩 전략을 취하고 페치조인을 사용하는 것이 N+1 문제를 해결하는 방법이자, 성능 최적화의 방법이라고 알고 있습니다. 그런데 JPA에서 엔티티 그래프를 사용해도 N+1 문제를 해결할 수 있지 않나요? 사용 방법도 더 편리하고 좋은 것 같은데, 엔티티 그래프를 사용하지 않는 이유가 있나요?
- 해결됨실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
트랜잭션의 범위와 준영속 상태
안녕하세요 강의를 수강하다 제가 잘못 이해하고 있는 부분이 있는 것 같아서 질문드리게 되었습니다.다음은 "간단한 주문 조회 V2: 엔티티를 DTO로 변환" 강의에서 35초에 해당하는 화면을 캡처한 부분입니다.orderRepository는 @PersistenceContext 처리된 EntityManager를 가지고 있는데 이 때 Transaction의 범위가 어디까지인지를 잘 모르겠습니다.추측으로는 List<Order> all가 준영속상태로 반환될 것으로 예상했는데 order가 membe프록시 객체를 가지고 있다가 Lazy 강제 초기화가 가능한 것을 보면 영속성 컨텍스트가 여전히 존재하는 상태인 것 같습니다.Transaction의 범위가 어디까지인지 궁금합니다.감사합니다.
- 해결됨실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
DTO를 Result<T>에 담아서 반환할 때의 제너릭 표기 여부
안녕하세요DTO를 직접 반환하지 않고 Result로 감싸서 반환하는 것을 구현하는 도중 궁금한 점이 생겼습니다. 일단 코드는 다음과 같습니다./** * DTO를 감싸서 반환하는 클래스 * 반환에 성공하면 status에는 "success", data에는 DTO, error에는 null이 담긴다 * 예외가 발생하면 status에는 "fail", data에는 null, error에는 errorCode가 담긴다 */ @NoArgsConstructor @Getter public class Result<T> { private String status; private T data; private ErrorDTO error; public Result(String status, T data, ErrorDTO error) { this.status = status; this.data = data; this.error = error; } } @PostMapping("/save") public ResponseEntity<Result<UserDTO>> save(@RequestBody @Validated UserDTO userDTO, BindingResult bindingResult) { if (bindingResult.hasErrors()) { for (ObjectError error : bindingResult.getAllErrors()) { return ResponseEntity.ok().body(ResultUtils.fail(error.getCode(), error.getDefaultMessage())); } } UserDTO savedUserDTO = userService.save(userDTO); return ResponseEntity.ok().body(ResultUtils.success(savedUserDTO)); }public class ResultUtils { private static final String SUCCESS = "success"; private static final String FAIL = "fail"; public static <T> Result<T> success(T data) { return new Result<>(SUCCESS, data, null); } // 이 메서드의 제너릭 표기도 어떻게 해야할지...? public static Result fail(String errorCode, String message) { return new Result(FAIL, null, new ErrorDTO(errorCode, message)); } } 이렇듯 예외가 발생한 경우에 data에는 null을 담아서 반환하고 싶으면, 예외 발생 여부에 따라 Result<T>의 T가 userDTO이기도 하고 null의 2가지...? 인 느낌이 드는데 그럼 public ResponseEntity<Result<UserDTO>> save처럼 메서드 선언부에서 Result의 제너릭을 표기하는 의미가 있을까요? Result로 적는 것이 좋을지 그래도 userDTO를 명시해 줘야할지가 궁금합니다. 추가로 userDTO 단건일시에는 제너릭이 그다지 복잡하지 않지만 만약 DTO에 페이징 기능을 더하여 반환하고자 한다면 Result<Page<UserDTO>> 처럼 제너릭 안에 제너릭이 있게 될텐데 이러한 경우에도 그냥 제너릭을 생략하는 것이 좋을지 궁금합니다!