묻고 답해요
141만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
MemberServiceTest Rollback 관련 질문
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]안녕하세요. 저는 롤백 false를 안해서 자동으로 rollback이 되는데 왜 insert 쿼리가 찍히는걸까요??
-
해결됨Practical Testing: 실용적인 테스트 가이드
트랜잭션이 보장되고 처리되어야만 하는 N개의 작업이 있다면 별도의 서비스로 추출하라는 말이 헷갈립니다.
안녕하세요, 항상 강의 잘 보고 있습니다. 강의 듣기 전에는 모든 Layer에 단위테스트를 작성하느라 테스트가 싫었는데 덕분에 테스트가 재미있어졌습니다. 감사합니다 😀 아래 질문에 대한 답변을 읽다가 알쏭달쏭한 부분이 생겼습니다.해당 질문(긴 작업일 경우 트랜잭션을 걸지 말아도 된다는 점이 이해가 잘 안갑니다)에서 강사님께서는 "메일 전송 이후에 해야하는 작업들이 1가지가 아니라 트랜잭션이 보장되고 처리되어야만 하는 N개의 작업이었다면, 별도의 서비스로 추출해 트랜잭션을 적용하는 방법을 생각해볼 수 있습니다." 라는 말씀을 하셨습니다. 그렇다면 아래 MailService 클래스를 예시로 생각했을 때, 아래와 같이 별도의 클래스로 분리하라는 말씀이 맞으실까요?// 변경 전 @RequiredArgsConstructor @Service public class MailService { private final MailSendClient mailSendClient; private final MailSendHistoryRepository mailSendHistoryRepository; public boolean sendMail(String fromEmail, String toEmail, String subject, String content) { boolean result = mailSendClient.sendEmail(fromEmail, toEmail, subject, content); if (result) { mailSendHistoryRepository.save(MailSendHistory.builder() .fromEmail(fromEmail) .toEmail(toEmail) .subject(subject) .content(content) .build() ); return true; } return false; } }아래 코드에서 변경된 부분은 다음과 같습니다.MailSendHistoryService 클래스에 대한 의존관계가 생겼습니다.MailService의 sendEmail 메서드에는 readOnly 트랜잭션이 걸렸습니다.MailSendHistoryService의 추가적인_일을_하다 메서드에는 트랜잭션이 걸렸습니다.// 변경 후(내 생각) @RequiredArgsConstructor @Service public class MailService { private final MailSendClient mailSendClient; private final MailSendHistoryService mailSendHistoryService; @Transactional(readOnly = true) public boolean sendMail(String fromEmail, String toEmail, String subject, String content) { boolean result = mailSendClient.sendEmail(fromEmail, toEmail, subject, content); if (result) { mailSendHistoryService.추가적인_일을_하다(); return true; } return false; } } @RequiredArgsConstructor @Service public class MailSendHistoryService { private final MailSendHistoryRepository mailSendHistoryRepository; @Transactional public void 추가적인_일을_하다() { ... DB CUD 작업 ... } }
-
미해결코드로 배우는 React with 스프링부트 API서버
동적페이이지 이동, 8강 10분56초에서 허공에 클릭하시는데 페이지가 이동하는것 같은데요...
8강 10분56초에서 하늘색 허공에 클릭하시는데 페이지가 이동하는것 같은데요. 어떻게 하신것인지 궁금해서 질문드립니다. 글자를 클릭해야 하는것 아닌가요? 근데 영상에서 동작은 또 잘되네요...?... 제가 잘 몰라서 이해를 잘못한걸까여?... 아래는 제 코드입니다..
-
미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
OSIV off인 아키텍쳐에서 궁금해서 질문드립니다.
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요.영한쌤이 말씀해주신Controller -> Application Service -> Domain Service -> Repository에서Application Service: 트랜잭션의 시작, Domian Service에 있는 순수 도메인 로직들을 활용하여, 복잡한 애플리케이션 비즈니스 로직을 구성Domain Service : 도메인에 대한 순수한 로직으로 구성. Application Service의 도구로서 활용됨. OSIV를 off 상태이기 때문에 Controller에 반환시 Application Service에서는 응답 Dto로 넘겨줘야 하는데 여기서 한가지 고민이 있습니다. 아키텍쳐는 위에서 아래로 순방향으로 흘러야 하는데 (역행, 순환 참초 X). Application Service에서 Controller단에 종속적인 응답 Dto를 넘기게 된다면, Application Service와 Controller간에 순환 참조와 역류 참조가 일어나지 않아 문제이지 않나 싶습니다... 상관이 없는지 아니면 어떻게 해결해야 좋을까요?
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
주문 서비스 테스트를 하고 있는데 어디에서 잘못된건지 잘 모르겠습니다..
package jpabook.jpashop.service; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import jpabook.jpashop.domain.Address; import jpabook.jpashop.domain.Member; import jpabook.jpashop.domain.Order; import jpabook.jpashop.domain.OrderStatus; import jpabook.jpashop.domain.item.Book; import jpabook.jpashop.domain.item.Item; import jpabook.jpashop.exception.NotEnoughStockException; import jpabook.jpashop.repository.OrderRepository; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; @RunWith(SpringRunner.class) @SpringBootTest @Transactional public class OrderServiceTest { @PersistenceContext EntityManager em; @Autowired OrderService orderService; @Autowired OrderRepository orderRepository; @Test public void 상품주문() throws Exception { //Given Member member = createMember(); Item item = createBook("시골 JPA", 10000, 10); //이름, 가격, 재고 int orderCount = 2; //When Long orderId = orderService.order(member.getId(), item.getId(), orderCount); //Then Order getOrder = orderRepository.findOne(orderId); assertEquals("상품 주문시 상태는 ORDER",OrderStatus.ORDER, getOrder.getStatus()); assertEquals("주문한 상품 종류 수가 정확해야 한다.",1, getOrder.getOrderItems().size()); assertEquals("주문 가격은 가격 * 수량이다.", 10000 * 2, getOrder.getTotalPrice()); assertEquals("주문 수량만큼 재고가 줄어야 한다.",8, item.getStockQuantity()); } @Test(expected = NotEnoughStockException.class) public void 상품주문_재고수량초과() throws Exception { //... } @Test public void 주문취소() { //... } private Member createMember() { Member member = new Member(); member.setName("회원1"); member.setAddress(new Address("서울", "강가", "123-123")); em.persist(member); return member; } private Book createBook(String name, int price, int stockQuantity) { Book book = new Book(); book.setName(name); book.setStockQuantity(stockQuantity); book.setPrice(price); em.persist(book); return book; } } 예상 개수가 8개이고 결과는 2개가 나오는데 이게 어디서 수정을 해줘야 할 지 감이 안 잡힙니다.. java.lang.AssertionError: 주문 수량만큼 재고가 줄어야 한다. Expected :8Actual :2<Click to see difference> at org.junit.Assert.fail(Assert.java:89) at org.junit.Assert.failNotEquals(Assert.java:835) at org.junit.Assert.assertEquals(Assert.java:647) at jpabook.jpashop.service.OrderServiceTest.상품주문(OrderServiceTest.java:47) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:76) at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84) at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75) at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86) at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97) at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329) at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293) at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306) at org.junit.runners.ParentRunner.run(ParentRunner.java:413) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69) at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38) at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11) at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35) at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235) at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
category 엔티티에선 부모 객체가 왜 필요한건가요?
현재 실전 스프링부트와 jpa활용1편을 수강중입니다. 엔티티 클래스 개발2 에서 카테고리 클래스를 만들 때, 부모 객체는 왜 생성하는지 이해가 되지 않습니다..! 이미 카테고리 클래스 자체가 부모 클래스가 되는게 아닌가요..?
-
해결됨실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
entity를 다만들고 실행을 해보는데 자꾸 에러가 나오는데 해결을 못하겠습니다.
jpa활용 강의를 듣던중에 entity를 다 만들고 실행을 해보는데 에러가 나오긴하는데 어떤게 문제인지 정확히 잘 모르겠습니다.. 실행하면 실행되다가 에러메시지가 나오면서 바로 종료되버리는데 뭐가 문제인지 모르겠습니다ㅠㅠ그리고 구글 드라이브 업로드로 파일을 올리려고 했는데 자꾸 gradle. 경로를 찾을 수 없다고 압축이 안됩니다.. 그래서 일단 메시지로 올려봅니다.Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.2024-01-07T20:07:18.943+09:00 ERROR 1872 --- [ main] o.s.boot.SpringApplication : Application run failedorg.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Property 'jpabook.jpashop.domain.Delivery.status' is annotated '@Enumerated' but its type 'jpabook.jpashop.domain.DeliveryStatus' is not an enumat org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1775) ~[spring-beans-6.1.1.jar:6.1.1]at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) ~[spring-beans-6.1.1.jar:6.1.1]at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) ~[spring-beans-6.1.1.jar:6.1.1]at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) ~[spring-beans-6.1.1.jar:6.1.1]at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.1.jar:6.1.1]at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) ~[spring-beans-6.1.1.jar:6.1.1]at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-6.1.1.jar:6.1.1]at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1173) ~[spring-context-6.1.1.jar:6.1.1]at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:936) ~[spring-context-6.1.1.jar:6.1.1]at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) ~[spring-context-6.1.1.jar:6.1.1]at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.2.0.jar:3.2.0]at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) ~[spring-boot-3.2.0.jar:3.2.0]at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) ~[spring-boot-3.2.0.jar:3.2.0]at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) ~[spring-boot-3.2.0.jar:3.2.0]at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) ~[spring-boot-3.2.0.jar:3.2.0]at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) ~[spring-boot-3.2.0.jar:3.2.0]at jpabook.jpashop.JpashopApplication.main(JpashopApplication.java:10) ~[classes/:na]Caused by: org.hibernate.AnnotationException: Property 'jpabook.jpashop.domain.Delivery.status' is annotated '@Enumerated' but its type 'jpabook.jpashop.domain.DeliveryStatus' is not an enumat org.hibernate.boot.model.internal.BasicValueBinder.prepareBasicAttribute(BasicValueBinder.java:805) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]at org.hibernate.boot.model.internal.BasicValueBinder.setType(BasicValueBinder.java:374) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]at org.hibernate.boot.model.internal.PropertyBinder.makePropertyAndValue(PropertyBinder.java:261) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]at org.hibernate.boot.model.internal.PropertyBinder.makePropertyValueAndBind(PropertyBinder.java:297) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]at org.hibernate.boot.model.internal.PropertyBinder.createBasicBinder(PropertyBinder.java:1169) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]at org.hibernate.boot.model.internal.PropertyBinder.bindBasic(PropertyBinder.java:1071) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]at org.hibernate.boot.model.internal.PropertyBinder.bindProperty(PropertyBinder.java:890) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]at org.hibernate.boot.model.internal.PropertyBinder.buildProperty(PropertyBinder.java:788) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]at org.hibernate.boot.model.internal.PropertyBinder.processElementAnnotations(PropertyBinder.java:709) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]at org.hibernate.boot.model.internal.EntityBinder.processIdPropertiesIfNotAlready(EntityBinder.java:977) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]at org.hibernate.boot.model.internal.EntityBinder.handleIdentifier(EntityBinder.java:305) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]at org.hibernate.boot.model.internal.EntityBinder.bindEntityClass(EntityBinder.java:231) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]at org.hibernate.boot.model.internal.AnnotationBinder.bindClass(AnnotationBinder.java:422) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]at org.hibernate.boot.model.source.internal.annotations.AnnotationMetadataSourceProcessorImpl.processEntityHierarchies(AnnotationMetadataSourceProcessorImpl.java:255) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]at org.hibernate.boot.model.process.spi.MetadataBuildingProcess$1.processEntityHierarchies(MetadataBuildingProcess.java:278) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:321) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:1432) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1503) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:75) ~[spring-orm-6.1.1.jar:6.1.1]at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:376) ~[spring-orm-6.1.1.jar:6.1.1]at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) ~[spring-orm-6.1.1.jar:6.1.1]at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396) ~[spring-orm-6.1.1.jar:6.1.1]at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:352) ~[spring-orm-6.1.1.jar:6.1.1]at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) ~[spring-beans-6.1.1.jar:6.1.1]at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) ~[spring-beans-6.1.1.jar:6.1.1]... 16 common frames omittedProcess finished with exit code 1
-
미해결실전! Querydsl
q파일 오류
q파일은 생성되었는데 사용이 안됩니다...
-
미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
섹션 4. API 개발 고급 - 컬렉션 조회 최적화 - 주문조회 V3
섹션 4. API 개발 고급 - 컬렉션 조회 최적화 - 주문조회 V3 수강 중에 질문 있어 글 남깁니다. 현재 스프링 부트 3.0.11 자바 17버전인데 강의에서는 findAllWithitem()에서 select에 distinct를 붙이지 않을 경우 총 네개의 Order가 생겼습니다. 패치 조인으로 인해 두개씩 생성되었기 때문에.하지만 저의 경우에는 distinct를 붙이지 않아도 두개의 Order가 생겼습니다. 혹시 스프링 버전 업에 따른 변경사항 일까요? 아니면 제가 코드를 잘못 친걸까요? 사진 첨부 하겠습니다.@GetMapping("/api/v3/orders") public List<OrderDto> ordersV3() { List<Order> orders = orderRepository.findAllWithItem(); for (Order order : orders) { System.out.println("order ref=" + order + " id="+order.getId()); } List<OrderDto> result = orders.stream() .map(o -> new OrderDto(o)) .collect(Collectors.toList()); return result; }public List<Order> findAllWithItem() { return em.createQuery( "select o from Order o" + " join fetch o.member m" + " join fetch o.delivery d" + " join fetch o.orderItems oi" + " join fetch oi.item i", Order.class ).getResultList(); }[ { "orderId": 1, "name": "userA", "orderDate": "2024-01-07T04:02:33.369413", "orderStatus": "ORDER", "address": { "city": "서울", "street": "1", "zipcode": "1111" }, "orderItems": [ { "itemName": "JPA1 BOOK", "orderPrice": 10000, "count": 1 }, { "itemName": "JPA2 BOOK", "orderPrice": 20000, "count": 2 } ] }, { "orderId": 2, "name": "userB", "orderDate": "2024-01-07T04:02:33.427993", "orderStatus": "ORDER", "address": { "city": "대구", "street": "2", "zipcode": "2222" }, "orderItems": [ { "itemName": "SPRING1 BOOK", "orderPrice": 30000, "count": 3 }, { "itemName": "SPRING2 BOOK", "orderPrice": 40000, "count": 4 } ] } ]
-
해결됨자바 ORM 표준 JPA 프로그래밍 - 기본편
양방향 관계에서
Member와 Team의 관계인 조건에서팀을 바꾼 다는 가정하에 Member의 TeamId 값을 바꿔보려고 코드를 구현 해봤는데생각하는대로 되지 안더군요. 어떻게 해야 하나요?Member findMember = em.find(Member.class, 3L); findMember.getTeam().setId(2L); tx.commit();Member에서 TeamId의 원래의 값이 1에서 2로 변경 하고자 하는 코드 입니다.업데이트 쿼리가 찍히지 않습니다. 도와주시면 감사하겠습니다.
-
해결됨자바 ORM 표준 JPA 프로그래밍 - 기본편
연관관계 주인과 cascade가 상충되는 상황에 대해서
제가 알고 있기론 연관관계 주인만 엔티티 수정이 가능하고 아니면 조회만 가능하다고 알고 있습니다.@OneToMany(mappedBy = "team") private List<Member> members = new ArrayList<>();이 코드에서 team은 member을 조회만 할수 있습니다.Team team = new Team(); team.setName("TeamA"); em.persist(team); Member member = new Member(); //역방향(주인이 아닌 방향)에서 멤버 추가 시도 team.getMembers().add(member); //실패! 그러나 cascade 또는 고아 객체 제거 옵션을 사용하면 부모 엔티티를 통해 자식 엔티티의 생명주기를 컨트롤 할 수 있다고 배웠습니다.@Entity public class Parent { @Id @GeneratedValue private Long id; @OneToMany(mappedBy = "parent", cascade = CascadeType.PERSIST) List<Child> childList = new ArrayList<>(); }Child child1 = new Child(); Child child2 = new Child(); Parent parent = new Parent(); child1.setParent(parent); //연관관계 추가 child2.setParent(parent); //연관관계 추가 parent.getChildList().add(child1); parent.getChildList().add(child2); //부모 저장, 연관된 자식들 저장 em.persist(parent); 두 개념이 서로 충돌하는것 같은데 어떻게 동작하는 것일까요?
-
해결됨실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
h2에 테이블 생성이 안돼요...ㅠ
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]프로젝트 환경설정을 마치고 프로젝트를 돌려봤습니다.똑같이 따라하면서요.그런데 사진과 같이 create문까지 잘 나오는데 h2에 테이블이 안보여요문제가 뭘까요? ㅠㅠ 주의 사항도 다 보면서 버전업 시켰는데도 안되네요..하루종일 구글링하고 찾아봐도 모르겠어요 도와주시면 감사하겠습니다
-
해결됨입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기
안녕하세요. 갑자기 Admin 으로 이동되어서 질문드립니다.
안녕하세요. 강의 잘 듣고 있습니다. 이 전단계가 Thymeleaf 레이웃 리팩토링이었는데, 갑자기 admin 의 interceptor 로 넘어와서요. 파일이 전부 개발되 있는 상태인데 영상 그대로 따라해도 되는건가요..?
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
TransactionContext 로그가 안 찍히는 이유
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]강의 화면을 보면 TransactionContext 에서" Rolled baxk transaction for test : .."라는 로그가 있는데제 거는 안 찍히면 어떤 문제일까요?
-
미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
예외처리2 validation Map
Validation 클래스를 만들어서 Validation 클래스를 List로 담아서 내보냈습니다.이렇게 하면 테스트가 정상적으로 동작을 하는데 Map을 사용했을 때 성능적으로나 효율성이 뭐가 더 나은지 알고 싶습니다.그리고 json 을 어떤 방식으로 넘겨야 좋은지 알고싶습니다.
-
미해결실전! Querydsl
동적 쿼리가 안되는 이유
@Repository @Log4j2 public abstract class Querydsl4RepositorySupport { // 이 클래스가 다루는 도메인(엔터티)의 클래스 private final Class domainClass; // 도메인 엔터티에 대한 Querydsl 쿼리를 생성하고 실행 private Querydsl querydsl; // 데이터베이스와의 상호 작용을 담당하는 JPA의 핵심 객체 private EntityManager entityManager; // queryFactory를 통해 Querydsl 쿼리를 생성하고 실행합니다. private JPAQueryFactory queryFactory; public Querydsl4RepositorySupport(Class<?> domainClass) { Assert.notNull(domainClass, "Domain class must not be null!"); this.domainClass = domainClass; } // Pageable안에 있는 Sort를 사용할 수 있도록 설정한 부분 @Autowired public void setEntityManager(EntityManager entityManager) { Assert.notNull(entityManager, "EntityManager must not be null!"); // JpaEntityInformation을 얻기 위해 JpaEntityInformationSupport를 사용합니다. // 이 정보는 JPA 엔터티에 대한 메타데이터 및 정보를 제공합니다. JpaEntityInformation entityInformation = JpaEntityInformationSupport.getEntityInformation(domainClass, entityManager); // 이는 Querydsl에서 엔터티의 경로를 생성하는 데 사용됩니다. SimpleEntityPathResolver resolver = SimpleEntityPathResolver.INSTANCE; // entityInformation을 기반으로 엔티티의 경로를 생성합니다. EntityPath path = resolver.createPath(entityInformation.getJavaType()); this.entityManager = entityManager; // querydsl 객체를 생성합니다. // 이 객체는 Querydsl의 핵심 기능을 사용할 수 있도록 도와줍니다. // 엔터티의 메타모델 정보를 이용하여 Querydsl의 PathBuilder를 생성하고, 이를 이용하여 Querydsl 객체를 초기화합니다. this.querydsl = new Querydsl(entityManager, new PathBuilder<>(path.getType(), path.getMetadata())); this.queryFactory = new JPAQueryFactory(entityManager); } // 해당 클래스의 빈(Bean)이 초기화될 때 자동으로 실행되는 메서드 @PostConstruct public void validate() { Assert.notNull(entityManager, "EntityManager must not be null!"); Assert.notNull(querydsl, "Querydsl must not be null!"); Assert.notNull(queryFactory, "QueryFactory must not be null!"); } // 이 팩토리는 JPA 쿼리를 생성하는 데 사용됩니다. protected JPAQueryFactory getQueryFactory() { return queryFactory; } // 이 객체는 Querydsl의 핵심 기능을 사용하는 데 도움이 됩니다. protected Querydsl getQuerydsl() { return querydsl; } // EntityManager는 JPA 엔터티를 관리하고 JPA 쿼리를 실행하는 데 사용됩니다. protected EntityManager getEntityManager() { return entityManager; } // Querydsl을 사용하여 쿼리의 SELECT 절을 생성하는 메서드입니다. // expr은 선택할 엔터티나 엔터티의 속성에 대한 표현식입니다. protected <T> JPAQuery<T> select(Expression<T> expr) { return getQueryFactory().select(expr); } // Querydsl을 사용하여 쿼리의 FROM 절을 생성하는 메서드입니다. // from은 엔터티에 대한 경로 표현식입니다. protected <T> JPAQuery<T> selectFrom(EntityPath<T> from) { return getQueryFactory().selectFrom(from); } // 이 메서드는 주어진 contentQuery를 사용하여 Querydsl을 통해 JPA 쿼리를 생성하고 실행하고, // 그 결과를 Spring Data의 Page 객체로 변환하는 기능을 제공 protected <T> Page<T> applyPagination(Pageable pageable, Function<JPAQueryFactory, JPAQuery> contentQuery) { // 1. contentQuery를 사용하여 JPAQuery 객체를 생성 JPAQuery jpaQuery = contentQuery.apply(getQueryFactory()); // 2. Querydsl을 사용하여 페이징 및 정렬된 결과를 가져옴 List<T> content = getQuerydsl().applyPagination(pageable, jpaQuery).fetch(); // 3. contentQuery를 다시 사용하여 countQuery를 생성 JPAQuery<Long> countQuery = contentQuery.apply(getQueryFactory()); // 4. countQuery를 실행하고 총 레코드 수를 얻음 long total = countQuery.fetchOne(); // 5. content와 pageable 정보를 사용하여 Spring Data의 Page 객체를 생성하고 반환 return PageableExecutionUtils.getPage(content, pageable, () -> total); } // 이 메서드는 contentQuery와 함께 countQuery를 인자로 받아서 사용합니다. // contentQuery를 사용하여 페이징된 결과를 가져오고, countQuery를 사용하여 전체 레코드 수를 얻습니다. protected <T> Page<T> applyPagination(Pageable pageable, Function<JPAQueryFactory, JPAQuery> contentQuery, Function<JPAQueryFactory, JPAQuery> countQuery) { JPAQuery jpaContentQuery = contentQuery.apply(getQueryFactory()); List<T> content = getQuerydsl().applyPagination(pageable, jpaContentQuery).fetch(); JPAQuery<Long> countResult = countQuery.apply(getQueryFactory()); log.info("countResult : " + countResult ); Long total = countResult.fetchOne(); return PageableExecutionUtils.getPage(content, pageable, () -> total); } } // count처리 까지 한것 public Page<Member> applyPagination2(MemberSearchCondition condition, Pageable pageable) { return applyPagination(pageable, contentQuery -> contentQuery.selectFrom(member) .join(member.team, team).fetchJoin() .where(userNameEq(condition.getUserName()), teamNameEq(condition.getTeamName()), ageGoe(condition.getAgeGoe()), ageLoe(condition.getAgeLoe()) ), countQuery -> countQuery .select(member.count()) .from(member) .where(userNameEq(condition.getUserName()), teamNameEq(condition.getTeamName()), ageGoe(condition.getAgeGoe()), ageLoe(condition.getAgeLoe())) ); } private BooleanExpression userNameEq(String userName) { return hasText(userName) ? member.userName.eq(userName) : null; } private BooleanExpression teamNameEq(String teamName) { return hasText(teamName) ? team.name.eq(teamName) : null; } private BooleanExpression ageGoe(Integer ageGoe) { return ageGoe != null ? member.age.goe(ageGoe) : null; } private BooleanExpression ageLoe(Integer ageLoe) { return ageLoe != null ? member.age.loe(ageLoe) : null; }서비스에서 public Page<MemberTeamDTO> search(MemberSearchCondition condition, Pageable pageable) { Page<Member> resultPage = memberTestRepository.applyPagination2(condition, pageable); return resultPage.map(member -> MemberTeamDTO.builder() .memberId(member.getId()) .age(member.getAge()) .userName(member.getUserName()) .teamId(member.getTeam().getId()) .teamName(member.getTeam().getName()) .build()); }컨트롤러 @GetMapping("/v3/members") public ResponseEntity<?> searchMemberV3(MemberSearchCondition condition, Pageable pageable) { Page<MemberTeamDTO> search = memberService.search(condition, pageable); return ResponseEntity.ok().body(search); }이렇게 작성했는데 뭐가 문제인지 페이지와 정렬은 잘되는데 http://localhost:9090/v3/members?teamName=teamA&page=1&sort=id,desc teamA와 teamB 모두 나오고 있습니다.질문1 : 조건이 안 먹고 있는데 왜 그럴까요? 어떤 조건으로 해도 안 먹고 있습니다.질문2 : 여기서 동적 쿼리에서 null 처리를 잘 해야 하는 이유가 null로 들어오면 무시되서 전부 조회가 되기 때문에 null 처리를 잘해야하는 거 맞나요?질문 3 : 예를들어, hasText(userName) ? member.userName.eq(userName) : null; 가 있으면 조건이 userName이 존재한다고 하면 true이지만 받아온 userName이 존재하지 않는 userName이면 null이 반환이 되나요? 질문 4 : null 문제 해결 글을 찾던 와중에@DataJpaTest public class DynamicQueryTest { JPAQueryFactory queryFactory; @Autowired EntityManager em; @BeforeEach void init() { queryFactory = new JPAQueryFactory(em); em.persist(new Member("userA", 10, "ROLE_MASTER")); em.persist(new Member("userB", 20, "ROLE_ADMIN")); em.persist(new Member("userC", 30, "ROLE_USER")); } @Test void dynamicQuery() { // Integer age = 10; // String role = "ROLE_MASTER"; Integer age = null; String role = null; List<Member> result = queryFactory .selectFrom(member) .where(ageAndRoleEq(age, role)) .fetch(); System.out.println("result = " + result); } private BooleanBuilder ageAndRoleEq(Integer age, String role) { return ageEq(age).and(roleEq(role)); } private BooleanBuilder ageEq(Integer age) { if (age == null) { return new BooleanBuilder(); } else { return new BooleanBuilder(member.age.eq(age)); } } private BooleanBuilder roleEq(String roleName) { if (roleName == null) { return new BooleanBuilder(); } return new BooleanBuilder(member.roleName.eq(roleName)); } } return nullSafeBuilder(() -> member.age.eq(age)); } private BooleanBuilder roleEq(String roleName) { return nullSafeBuilder(() -> member.roleName.eq(roleName)); } public static BooleanBuilder nullSafeBuilder(Supplier<BooleanExpression> f) { try { return new BooleanBuilder(f.get()); } catch (IllegalArgumentException e) { return new BooleanBuilder(); } }이렇게 줄일 수 있다고 나왔는데 수업에서는 BooleanExpression을 사용했지만 null 방지를 위해서 BooleanBuilder 을 사용하는건가요? 이렇게 하면 null 방지가 되면서 전체 조회가 안되는 형식인가요?
-
해결됨실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
H2 데이터베이스 설치 강의에 연결 오류
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]H2 데이터베이스 설치 강의에서 jpashop.mv.db 파일 생성 확인 후 JDBC URL: 변경을 하면 Connection is broken: "java.net.ConnectException: Connection refused: no further information: localhost" [90067-224] 이라는 에러가 뜨면서 연결되지 않습니다. 어떻게 해결해야 될까요?
-
미해결Practical Testing: 실용적인 테스트 가이드
이런 경우는 어떻게 테스트 해야할까요?
지금 토스페이먼트 결제를 연동하고있는데 토스에서 결제 요청-인증을 하면 토스에서 주소에 파라미터로 paymentKey을 주는데 이 paymentKey을 가지고 백엔드에서 토스의 api에 결제 승인 요청을 해야합니다.그런데 서비스 단에서는 컨트롤러에서 파라미터을 통해 paymentKey을 알 수 있지 서비스단의 테스트 코드 작성 시는 paymentKey을 알 수 가 없는데 이런 경우는 테스트 코드를 어떻게 작성해야할까요? 추가적으로 외부 api 에서 paymentkey을 파라미터로 주는 컨트롤러는 어떻게 테스트해야할까요?
-
미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
레이어드 아키텍쳐에 대해 질문 드립니다
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]보통 spring mvc 구조에서View -> Controller -> Service -> Repository로 구성되어있는데, 여기서 Dto 변환하는 곳에서 고민이 있었습니다. Dto를 Service까지 끌고 가서, Service에서 엔티티로 변환 과정을 거칠것인지. 아니면 Controller단에서 dto - Entity 변환 과정을 거칠것인지 고민이 있었습니다Service 단에서 변환 과정을 거치게 된다면, 즉 파라미터로 해당 컨트롤러 한곳만 종속적이게 되는 고민이 있게 되었습니다. Service는 여러곳에서도 쓸 수 있다는건데, Dto를 Service 까지 끌고 오게 되면, 매번 서비스 메소드를 만들어야 하는 문제가 있습니다.그래서 Controller와 Service 사이에Controller -> Business(Service) -> Service 라는 비즈니스를 추가하여 설계를 하였습니다 Business : dto - entity 변환 로직 처리. 컨트롤러와 상호작용하여 , dto를 받아 entity로 변환 다시 dto를 반환하도록 만들었고, 여기서는 여러 Service의 메소드를 가져와서 복잡한 비즈니스 로직을 처리하도록 하였습니다.Service : Entity를 받고 Entity를 반환하면, 순전히 해당 도메인의 비즈니스 로직으로만 구성하도록 하였습니다.이런 식으로 했더니, 좀 더 유연하게 비즈니스 로직을 처리할 수 있더라구요. 제가 설계한 레이어 아키텍쳐 (dto 변환)이 맞는 설계인지, 이렇게도 실무에서 구성하는지 궁금합니다.
-
미해결
사용자가 기존 데이터 수정시에 null 구분 어떻게하시나요?
회원이 회원정보를 변경하였습니다.변경된 데이터는 "자기소개" 부분이며, 값을 비워버렸습니다.그럼 백엔드 입장에서는 아래 두가지 경우를 어떻게 구분할 수 있을까요?이 경우 사용자가 값을 비운건지?그게 아니면, 수정을 안해서 null 인 건지 제가 찾은 방법은수정시에도 모든 컬럼 갑을 받아서 기존 DB의 로우 전체를 업데이트 시킨다.사용자가 의도적으로 필드를 비워둔 경우 null 이 아닌, null을 의미하는 다른 대체 문자를 이용한다.- 이 경우 null 은 필드변경이 없는 경우 이외에 보편적으로 이용하는 방법이나 스마트한 방법이 있을까요...?