묻고 답해요
161만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
RequestMappingHandlerAdapter가 V1,V2,V3의 핸들러들을 모두 처리할 수 있는 이유
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]SpringMemberControllerV1,V2,V3에서 newForm, save, members메서드들이 RequestMappingHandlerMapping에 핸들러 매핑정보로 등록되어있고,RequestMappingHandlerAdapter 가 V1,V2,V3들의 모든 매서드들을 처리하는 것으로 이해하였습니다. 여기서SpringMemberControllerV1, V2, V3의 newForm, save, members메서드들은 인자값과 반환값이 다릅니다.V1,V2인자: HttpServletRequest , HttpServletResponse반환: ModelAndView V3인자: RequestParam, Model반환: String하나의 어뎁터로 인자값과 반환값이 다른 핸들러들를 모두 처리할 수 있는 점이 이해가 잘 되지 않았습니다.RequestMappingHandlerAdapter 를 살펴보았을때, HttpServletRequest , HttpServletResponse 를 인자로 받고 ModelAndView 를 반환하는 handleInternal 함수만 찾을 수 있었습니다.위의 3가지 이유로 `RequestMappingHandlerAdapter V1,V2 버전을 처리하는 것은 이해하였지만, V3를 처리할 수 있는 이유가 무엇인지 궁금합니다. 제가 추측하는 이유는 다음과 같습니다.RequestMappingHandlerAdapter 가 V1,V2를 처리하고 V3를 따로 처리하는 어뎁터가 있나요?RequestMappingHandlerAdapter 가 V1,V2,V3를 모두 처리할 수 있게하는 애노테이션의 기능이 있나요?
-
해결됨실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
stock_quantity 동시성 해결하는 방법에 대해
@Test public void 상품_주문() { //given Member member = new Member(); member.setName("회원1"); member.setAddress(new Address("서울", "강가", "123-123")); em.persist(member); Book book = new Book(); book.setName("시골 JPA"); book.setPrice(10000); book.setStockQuantity(10); book.setAuthor("kim"); em.persist(book); //when int orderCount = 2; // 두권 주문 Long orderId = this.orderService.order(member.getId(), book.getId(), orderCount); //then Order getOrder = this.orderRepository.findOne(orderId); Assertions.assertEquals(OrderStatus.ORDER, getOrder.getStatus()); }강의 내용 중 테스트 코드//==생성 메서드==// 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; }강의 내용 중 OrderItem 엔티티 내 생성 메서드 createOrderItem()강의에서는 위와 같이 OrderItem 엔티티의 생성 메서드라는 것을 통해 주문상품을 만들고 상품의 재고를 감소시키는데요. jpa가 상품 수량(stock_quantity) 감소시킬 때 사용한 UPDATE 쿼리를 보니까 UPDATE item SET stock_quantity = 8; 과 같이 되어있더라고요. 이러면 여러 클라이언트가 동시에 해당 상품 주문할 때 덮어쓰는 문제가 발생하니까 따로 해결 방법을 찾아봤습니다.JPA에 낙관적 락이라는 게 있길래 적용해봤더니 다른 트랜잭션이 중간에 상품 수량을 변경하고 커밋하면 해당 트랜잭션에서 변환된 스프링 예외(ObjectOptimisticLockingFailureException)가 올라오며 덮어쓰는 문제는 막을 수 있었습니다(테이블 데이터 생성 후 ddl-auto: none 모드로 실행, h2 콘솔과 함께 테스트). 예외를 잡고 새 스냅샷으로 다시 호출할 수 있겠지만, db에 stock_quantity 데이터만 정확히 맞추면 되는 게 목적이어서 createOrderItem 로직을 별도의 리포지토리로 대체해보았습니다.@Slf4j @Repository @RequiredArgsConstructor public class OrderItemRepository { private final EntityManager em; /** * 주문 상품 생성 * @param item * @param orderPrice * @param count * @return */ public OrderItem createOrderItem(Item item, int orderPrice, int count) { OrderItem orderItem = new OrderItem(); orderItem.setItem(item); orderItem.setOrderPrice(orderPrice); orderItem.setCount(count); int restStock = item.getStockQuantity() - count; if (restStock < 0) { log.info("need more stock for {}.{}", item.getId(), item.getName()); throw new NotEnoughStockException("need more stock"); } // 해당 상품의 재고 수량 차감 em.createQuery("UPDATE Item i " + "SET stock_quantity = stock_quantity - :count " + "WHERE item_id = :item_id") .setParameter("count", count) .setParameter("item_id", item.getId()) .executeUpdate(); // em.refresh(item); return orderItem; }em.createQuery()로 item.removeStock(count); 부분을 바꿨습니다. SET stock_quantity = stock_quantity - :count /** OrderService 내 order 메서드 */ @Transactional(readOnly = false) public Long order(Long memberId, Long itemId, int count) { //엔티티 조회 Member member = this.memberRepository.findOne(memberId); Item item = this.itemRepository.findOne(itemId); //배송 정보 생성 Delivery delivery = new Delivery(); delivery.setAddress(member.getAddress()); //주문 상품 생성 OrderItem orderItem = this.orderItemRepository.createOrderItem(item, item.getPrice(), count); //주문 생성 Order order = Order.createOrder(member, delivery, orderItem); //주문 저장 this.orderRepository.save(order); return order.getId(); }기존에 호출하던 OrderItem.createOrderItem(item, item.getPrice(), count); 대신에 orderItemRepository.createOrderItem()를 호출하도록 OrderService 코드를 변경강의에서 작성한 상품_주문() 테스트를 단일 실행했을 때도 em.createQuery.executeUpdate()할 때 보니까, flush인가 그것도 호출안했는데 jpa가 그전에 persist한 book이랑 member까지는 실제로 인서트하고 업데이트 쿼리를 실행하는 것을 확인했습니다.[질문]1. 강의에선 OrderItem 엔티티 자체에서 createOrderItem()을 처리해주기 때문에 OrderItem의 기본 생성자도 protected로 지정해서 막았는데, 글에서 적용한 방식으로 해결하려면 public으로 바꿔야 했습니다. 지금처럼 OrderItemRepository를 따로 만들어서 처리하는 게 구조상 문제가 없는 건지 리포지토리의 역할이 맞는지 모르겠고, 문제가 생길 수 있는지 궁금합니다.(예측되는 문제점: 주문/주문 취소/상품 수정 등 stock_quantity와 얽혀있는 로직마다 쿼리를 작성해야 됨) 2. JPA의 기본적인 변경 감지 방식을 유지하면서 낙관적 락 없이도 간단하게 해결할 수 있는 방법이 있는지 알고 싶습니다.
-
미해결스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
혹시 강의 중에 PDF 파일 관련해서는 다운 받을 수 없나요?
혹시 pdf 파일은 어디서 다운 받을 수 있나요?
-
해결됨스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
import jakarta.persistence.Entity; jpa 관련 import에러
JPA Member 작성 중 오류가 발생하여 질문 드립니다.import javax.persistence.Entity;import jakarta.persistence.Entity;모두 시도 해봤는데 안됩니다.gradle 프로젝트를 reload 하더라도 Unable to load class 'org.gradle.api.plugins.MavenPlugin'.This is an unexpected error. Please file a bug containing the idea.log file.이런 에러만 계속 발생합니다.maven플러그인이 사라졌다고 해서 gradle 버전변경도 시도해보고 자바 버전 변경도 시도해 봤지만 해결하지 못했습니다.
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
WebConfig 구조질문
아래 코드와 같이 webconfig 에 대한 코드를 치고 봤더니 3번째 메서드 logFilter()는 @Bean을 붙여주어서 이 주석을 해제하면 웹설정이 동작하는건 이해가 가는데 첫번째 두번째 메서드는 WebMvcConfigurer 인터페이스의 메서드들을 오버라이딩해서 구현하고 @bean을 안붙였는데 웹 설정이 어떻게 적용이 되는지 궁금합니다.@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LogInterceptor()).order(1).addPathPatterns("/**").excludePathPatterns("/css/**","*.ico","/error-page/**") ; //오류 페이지 경로 제외 } @Override public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) { resolvers.add(new MyHandlerExceptionResolver()); } //@Bean public FilterRegistrationBean logFilter(){ FilterRegistrationBean<Filter> filterFilterRegistrationBean = new FilterRegistrationBean<>(); filterFilterRegistrationBean.setFilter(new LogFilter()); filterFilterRegistrationBean.setOrder(1); filterFilterRegistrationBean.addUrlPatterns("/*"); filterFilterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST,DispatcherType.ERROR); return filterFilterRegistrationBean; }
-
미해결스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
support되는 핸들러 어댑터가 여러개일때는 어떻게 처리해야할까요??
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]안녕하세요, 강의를 듣다가 궁금한점이 생겼습니다.이제 핸들러 어댑터에서 support를 사용했는데 해당 컨트롤러를 처리할 수 있는 핸들러 어댑터가 2개 이상일때는 어떤 식으로 처리를 해주는게 똑똑하게 처리한걸까요?
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
testMember 시 오류 발생 (MySQL 사용)
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요.구글 드라이브에 전체 코드 업로드하였습니다. (properties.yml 에서 MySQL password만 지우고 올렸습니다.)https://drive.google.com/drive/folders/1az491IbBQQf5nqE9DsPHk3NHG1oM5Xg2?usp=drive_link 안녕하세요. 저는 h2 대신 MySQL을 사용하며 실습을 진행하고 있습니다.다름이 아니라, 강의와 동일하게 진행하고 있는데, 계속 오류가 떠서 진행이 힘든 상황입니다.org.springframework.dao.InvalidDataAccessResourceUsageException: could not execute statement; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not execute statementCaused by: org.hibernate.exception.SQLGrammarException: could not execute statementCaused by: java.sql.SQLSyntaxErrorException: Table 'jpa-shop.members' doesn't exist위와 같은 오류가 발생합니다. (중간에 자잘자잘한 코드들은 제외했습니다.)MySQL에서 member 가 예약어라고 해서 @Table(name="Members)를 붙여주었지만, 소용이 없었습니다.코드 첨부하겠습니다.MemberMemberRepository여기서 em 필드에 색깔이 안 들어오는 것도 뭔가 신경쓰입니다.application.yml캡처할 때만 password를 지우고 캡처했습니다. MemberRepositoryTest위에서 save에 빨간 밑줄이 뜨고 있습니다. (Junit5 사용중입니다)도와주시면 감사하겠습니다..!! 혹시 몰라서 전체 오류 로그를 복붙하겠습니다.org.springframework.dao.InvalidDataAccessResourceUsageException: could not execute statement; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not execute statementat org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:259)at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:233)at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:551)at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:152)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708)at jpabook.jpashop.MemberRepository$$EnhancerBySpringCGLIB$$3abfe072.save(<generated>)at jpabook.jpashop.MemberRepositoryTest.testMember(MemberRepositoryTest.java:31)at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.base/java.lang.reflect.Method.invoke(Method.java:566)at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725)at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:214)at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:210)at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66)at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:57)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:232)at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)Caused by: org.hibernate.exception.SQLGrammarException: could not execute statementat org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:63)at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:37)at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:113)at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:99)at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:200)at org.hibernate.dialect.identity.GetGeneratedKeysDelegate.executeAndExtract(GetGeneratedKeysDelegate.java:58)at org.hibernate.id.insert.AbstractReturningDelegate.performInsert(AbstractReturningDelegate.java:43)at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3279)at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3914)at org.hibernate.action.internal.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:84)at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:645)at org.hibernate.engine.spi.ActionQueue.addResolvedEntityInsertAction(ActionQueue.java:282)at org.hibernate.engine.spi.ActionQueue.addInsertAction(ActionQueue.java:263)at org.hibernate.engine.spi.ActionQueue.addAction(ActionQueue.java:317)at org.hibernate.event.internal.AbstractSaveEventListener.addInsertAction(AbstractSaveEventListener.java:329)at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:286)at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:192)at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:122)at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:185)at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:128)at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:55)at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:107)at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:756)at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:742)at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.base/java.lang.reflect.Method.invoke(Method.java:566)at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:315)at com.sun.proxy.$Proxy97.persist(Unknown Source)at jpabook.jpashop.MemberRepository.save(MemberRepository.java:15)at jpabook.jpashop.MemberRepository$$FastClassBySpringCGLIB$$a3e1a60b.invoke(<generated>)at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137)... 74 moreCaused by: java.sql.SQLSyntaxErrorException: Table 'jpa-shop.members' doesn't existat com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:121)at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:916)at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1061)at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1009)at com.mysql.cj.jdbc.ClientPreparedStatement.executeLargeUpdate(ClientPreparedStatement.java:1320)at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdate(ClientPreparedStatement.java:994)at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeUpdate(HikariProxyPreparedStatement.java)at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:197)... 106 more
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
testMember() 테스트 실행 시 entityManagerFactory 생성하는데 에러 발생했다고 나옵니다.
h2 설치하고 설정 후 테스트 코드 작성 부분에서 다음과 같은 문제가 발생합니다.애플리케이션을 실행해도 똑같은 에러가 발생합니다.org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.MappingException: Could not instantiate id generator [entity-name=jpabook.jpashop.Member]build.gradleplugins { id 'java' id 'org.springframework.boot' version '3.1.3' id 'io.spring.dependency-management' version '1.1.3' } group = 'jpabook' version = '0.0.1-SNAPSHOT' java { sourceCompatibility = '17' } 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-validation' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-devtools' compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.h2database:h2' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation("org.junit.vintage:junit-vintage-engine") { exclude group: "org.hamcrest", module: "hamcrest-core" } } tasks.named('test') { useJUnitPlatform() } h2 접속 화면application.yml 파일spring: datasource: url: jdbc:h2:tcp://localhost/~/jpashop username: sa password: driver-class-name: org.h2.Driver jpa: hibernate: ddl-auto: create properties: hibernate: format_sql: true logging.level: org.hibernate.orm.jdbc.bind: traceMember 클래스package jpabook.jpashop; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; import lombok.Getter; import lombok.Setter; @Getter @Setter @Entity public class Member { @Id @GeneratedValue private Long id; private String username; }
-
미해결스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
Annotation Processors 설정
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]프로젝트를 새로 생성한 다음에 매번 Annotaion Processors를 체크 해주는걸로 알고있는데, 체크를 안해줘도 lombok 사용이나 코드가 실행되는데 문제가 없었던것 같습니다. 혹시 어떤 이유로 해당 기능을 체크해주는 건가요?
-
미해결스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
보안에 관련한 한가지 의문점이 생기네요
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]다른 방식도 물론이겠지만GET방식으로 데이터를 전송하다 보면간단한 해킹 공격으로도 해킹당하기 매우 쉬울거같은데이런 부분은 Spring Security를 배우면 알수있는건가요? 아니면 네트워크 보안쪽으로 따로 배우는 무언가가 있는건가요 ?
-
해결됨스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
session 객체를 thymeleaf 에서 어떻게 꺼낼 수 있죠?
@GetMapping("/basic-objects") public String basicObjects(HttpSession session) { ... }위 컨트롤러 작성하는 부분입니다.관련된 html 파일은 basic-objects 인데, ${#session} 으로 세션을 꺼내고 ${session} 으로도 세션을 꺼내네요.이게 가물가물한데 컨트롤러 메서드에서 파라미터로 호출하면 자동으로 모델에 넣어주기 때문에 thymeleaf 에서 ${session} 을 꺼낼 수 있는거죠?
-
미해결재고시스템으로 알아보는 동시성이슈 해결방법
lettuce 클라이언트는 락 획득
안녕하세요 강사님. 강의 잘 듣고 있습니다. 궁금한 부분이 있는데요.lettuce 클라이언트는 락 획득을 스핀락을 돌면서 자체적으로 얻어온다고했는데 그렇다면 어떻게 애플리케이션의 비니지스 코드까지 락을 얻지못했다는 값을 넘겨주나요?while(!redisLockRepository.lock(id)) 구문이 어떻게 실행될 수 있는지 모르겠습니다.왜냐하면 위에서 말한것처럼 lettuce 클라이언트에서 락을 얻기 위해 내부적으로 계속 스핀락을 돌다가 결국 얻어내서 true 를 리턴할것으로 예상되기 때문인데요.답변 부탁드리겠습니다
-
미해결스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
메뉴얼 강의자료는 어디서 다운받을수 있나요?
메뉴얼 강의자료는 어디서 다운받을수 있나요?강의들으면서 간단한 코드들부터 계속 손으로 치고 스샷으로 남겨놓으면서 공부하고있는데 메뉴얼 자료를 받을 수 있나요?
-
해결됨실전! 스프링 부트와 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 영역에서 비즈니스 로직을 처리해도 될까요? 너무 글이 장황하고 기네요.. ㅠㅠ 혹시 도움을 주신다면 너무나도 감사드리겠습니다.
-
미해결자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지]
안녕하세요 5강 진행중에 RestController를 어노테이션할때 오류가 발생합니다
5강 진행중 RestController를 @RestController 할때 오류가 발생합니다.위에 import 를 추가하였으나, 이와 같은 에러가 발생합니다 어떻게 하면 될까요?
-
해결됨스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
${item.itemName}에 질문이 있습니다.
SpringEL강의에서 이해한 바로는 ${item.itemName}은itemName필드의 Getter를 호출해서 그 값을 받아오는걸로 이해했는데 강의에서 th:field="${item.itemName}"을 했을 때 id 와 name값이 "itemName"인지 이해가 안됩니다.
-
미해결Practical Testing: 실용적인 테스트 가이드
공통관심사 테스트 가이드
안녕하세요. 우빈님질문이 있습니다! AOP, Interceptor... 와 같은 공통 관심사를 테스트할 때는 보통 어떤 전략을 갖고 테스트 코드를 작성하나요?예를 들어 아래와 같은 요구사항이 있다고 가정합니다.22시 이후에는 모든 요청에 대해서 예외를 발생시킨다. 저라면 모든 Controller에 check22hours()라는 로직을 AOP를 사용하여 적용할 것 같은데요.이때 순수하게 check22hours()라는 단위 테스트만 작성하면 되는 것 일까요? 아니면 AOP 자체가 의도한대로 동작하는지 테스트 코드를 작성하는 것이 바람직한 것 인가요? 감사합니다.
-
미해결스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
단축키 질문
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요. 단순하고 실용적인 컨트롤러 - v4 강의10분23초쯤에F2를 눌러서 파라미터 추가하는게 있는데 어떻게 하는거예요? 해도 안되는데여,,ㅠㅠ
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
@GetMapping에 경로 입력 시에 사용되는 '/'
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]안녕하세요 질문이 있어서 글 남겨요.BasicController에서 @GetMapping의 경로를 입력해줄 때 @GetMapping("/variable")와@Getmapping("text-basic")으로 이 둘은 '/'의 유무로 차이점이 생기는데 '/'를 붙이는 것과 안 붙이는 것의 차이를 알 수 있을까요?
-
미해결스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
2023년에 강의를 들으시는 분들 중 View 환경설정 강의에서 thymeleaf URI 설정 오류 해결법 중 하나
질문은 아니고 해결한 문제가 있어서 커뮤니티 질문 글에라도 올립니다![질문 내용]View 환경설정 강의에서 thymeleaf URI 설정 오류 해결법 중 하나를 찾아서 말씀드립니다 현재는 2023.08.28 이고강의 초반에 IntelliJ community 버전을 사용해도 괜찮다고 하셔서 community 버전을 사용하고 있었는데인텔리제이 커뮤니티 버전을 사용하게되면<html xmlns:th="~thymeleaf.org"> 의 thymeleaf uri가 안 가져와집니다. 그 이유는 인텔리제이 community버전에선 application.properties를 지원하지 않기 때문인 것 같습니다.그래서 IDEA 버전으로 변경한 뒤잘 돌아갑니다! 그럼 참고하시고 저와 같이 시간 날리지 않으셨으면 좋겠습니다열공하세요!