묻고 답해요
158만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
LAZY 관계에서의 프록시 초기화에 대해 질문 있습니다.
Member와 Team이 있는데 이 둘이 다대일 관계이고 LAZY 설정이라고 하겠습니다.Member을 조회하면 Member의 필드인 team에는 Team에 대한 프록시 객체가 저장된다고 이해했습니다. 여기서 궁금한 부분이 있습니다.Member 조회시 생성되는 Team에 대한 프록시 객체는 영속성 컨텍스트에 저장이 되는건가요?team의 실제 값을 사용하면 프록시 초기화가 발생해 DB에서 실제 Team 엔티티 값을 가져올텐대요 이때 가져온 Team 엔티티는 어디서 저장되고 관리 되나요? 이미 영속성 컨텍스트에는 프록시 객체가 존재할텐대 가져온 실제 엔티티 객체는 어디서 저장되고 관리되는지 궁금합니다.
-
해결됨실전! 스프링 데이터 JPA
Projection 관련 질문이 있습니다.
영한님의 예시인 UsernameOnly에서는 유저명만 가져왔는데, 혹시 조금 더 깊게 들어가서 유저명뿐만 아니라 해당 유저가 속한 팀의 팀명까지 같이 가져올 수 있는 방법이 있을까요?public interface UsernameAndTeam { String getUsername(); Team getTeam(); }이렇게 하니 팀 객체까지는 잘 가져오던데, 여기서 팀의 이름만 딱 정해서 가져올 수도 있는지 궁금합니다.머릿속으론 String getTeamGetName(); 이라는 무슨 괴상한 코드가 떠오르는데.. 이건 제가 봐도 좀 아닌 것 같네요 ㅋㅋㅋㅠㅠ항상 답변 감사드립니다. ++ 혹시 몰라서 String getTeamGetName()으로 테스트를 실행해보니 No property 'getName' found for type 'Team'; 이라고 오류가 떴는데, 이 말은 Team에서 getName을 못 찾았다는 것 같은데 그럼 Team까진 잘 접근이 된 것 같으니 그럼 혹시 getTeamName이라고 하면 되지 않을까? 라는 생각이 들어 그렇게 바꿔 실행해보니 잘 되는 것 같네요..?! 이렇게 하면 되는 게 맞을까요?
-
미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
Controller + RestController 함께 사용
안녕하세요! 스프링 로드맵을 수강하고 Spring Boot + Thymeleaf 로 간단한 프로젝트를 진행하던 중 궁금한 점이 있어 질문 남김니다. 좋아요 기능이나 모달창에서 일어나는 동작들은 매번 결과를 view를 넘겨주기 번거롭거나 처리하기 복잡한 것 같아서, 이러한 부분들만 일반 Controller 말고 Rest Controller를 호출하고 처리하도록 구현하고 싶은데 혹시 이런 방식으로 혼용해서 사용해도 괜찮을까요? 아니면 프로젝트의 일관성을 위해 일반 Controller만 사용하는 것이 좋을까요? 감사합니다.
-
해결됨입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기
ProjectSkill과 Skill 객체 생성 관련 질문있습니다.
테스트코드 작성하는 강의를 막 마치고, html '/test' 생성하여 확인하는 강의를 진입하여 듣던중,test만 돌리다 오랜만에 datainitilizer가 불리도록 run을 하니,org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataInitializer': Invocation of init method failedCaused by: org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation : com.mkim.portfolio.domain.entity.ProjectSkill.skill -> com.mkim.portfolio.domain.entity.Skill 이런 에러가 발생하였습니다.테스트코드 작성 강의전에는 정상적으로 datainitializer 호출되었고, 테스트 케이스도 전부 패스한 상황이라 무엇이 문제인가 계속 확인을 하는데 이해한 바로는 ProjectSkill이 Skill 엔티티를 참조해야하지만 아직 생성이 안된 상태라 발생하는것처럼 이해하였는데, val java = Skill(name = "Java", type = SkillType.LANGUAGE.name, isActive = true) val kotlin = Skill(name = "Kotlin", type = SkillType.LANGUAGE.name, isActive = true) val python = Skill(name = "Python", type = SkillType.LANGUAGE.name, isActive = true) val spring = Skill(name = "Spring", type = SkillType.FRAMEWORK.name, isActive = true) . . . skillRepository.saveAll( mutableListOf( java, kotlin, spring, . . . ) . ... ..... project1.skills.addAll( mutableListOf( ProjectSkill(project = project1, skill = java), ProjectSkill(project = project1, skill = spring), ProjectSkill(project = project1, skill = mysql), ProjectSkill(project = project1, skill = redis) ) )이렇게 작성하여 부르면 에러가 나지만val skills = mutableListOf( Skill(name = "Java", type = SkillType.LANGUAGE.name, isActive = true), Skill(name = "Kotlin", type = SkillType.LANGUAGE.name, isActive = true), Skill(name = "Python", type = SkillType.LANGUAGE.name, isActive = true), Skill(name = "Spring", type = SkillType.FRAMEWORK.name, isActive = true), Skill(name = "Django", type = SkillType.FRAMEWORK.name, isActive = true), Skill(name = "MySQL", type = SkillType.DATABASE.name, isActive = true), Skill(name = "Redis", type = SkillType.DATABASE.name, isActive = true), Skill(name = "Kafka", type = SkillType.TOOL.name, isActive = true) ) skillRepository.saveAll(skills) . ... ..... project1.skills.addAll( mutableListOf( ProjectSkill(project = project1, skill = skills[0]), // Java ProjectSkill(project = project1, skill = skills[3]), // Spring ProjectSkill(project = project1, skill = skills[5]), // MySQL ProjectSkill(project = project1, skill = skills[6]) // Redis ) ) 이와 같이하면 에러가 살아집니다...무엇이 달라 차이를 만드는지가 너무 궁금하여 질문 남겨요감사합니다, 전체 현재까지 코드 깃허브
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
QueryDsl관하여 질문 드립니다.
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)네2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)네3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)네[질문 내용]안녕하세요. 근래에 쿼리 DSL을 배우면서 적용을 해보고 있습니다! 쿼리dsl을 사용하면 @OneToMany 등등 연관관계 매핑이 필요 없는지 궁금합니다. 필요하다면 왜 필요한지도 궁금합니다!!
-
해결됨실전! Querydsl
설명이 잘 이해가 안 가는 부분이 있습니다
강의 2분 45초에서 left join이니 team 기준으로 데이터를 전부 조회해온다고 말씀하셨는데, 그 team 기준이라는 말씀이 무슨 의미인 건지 잘 모르겠습니다.제가 생각하기로는 이 테스트의 경우 member와 team의 left join이니 member의 데이터들을 기준으로 조인된 team의 데이터를 조회해오는, 즉 member 기준으로 데이터를 조회하는 것 같은데, 제가 설명을 잘못 이해한 건지 아니면 제가 잘못 알고 있는 부분이 있는 건지 궁금합니다.감사합니다.
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
delivery 인식 불가 ㅠ
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오) 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)에3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)예[질문 내용]여기에 질문 내용을 남겨주세요.위 강의를 듣고잇는데, delivery가 제 개발환경에서는 인식이 안됩니다...선언부 미인식 문제 자주 하는 질문을 참고해서 IDE 초기화도 해보고 했는데, 안되서 질문드립니다. 뭔가 설정을 잘못한거 같아서 JDK와 Gradle 설정 부분도 제대로 되어 있는 것 확인했습니다.
-
미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
ManyToOne 에서 fetch join 에 대해 질문 있습니다.
강의 1분쯤에 아래와 같은 내용이 있습니다."ManyToOne이나 OneToOne 같은건 fetch join을 하거나 DB 입장에서는 left out join해서 한번에 데이터를 다 끌고와도 성능에 무리가 없는데 컬렉션 조회 같은 경우에는 조회를 하는 순간 DB 입장에서는 데이터가 뻥튀기 된다."궁금한 부분은 ManyToOne 관계에서 fetch join에서도 DB 입장에서 데이터가 뻥튀기 되지 않나 라는 생각입니다.예를들어 member1, member2, member3이 teamA에 속해 있다고 했을때 이 상황에서 fetch join을 해도 어차피 내부적으로는 join으로 바뀌게 되므로 결과적으로는 아래와 같이 출력이 되므로 DB 입장에서 결국 이 상황도 데이터 뻥튀기 이지 않나 라는 생각이 듭니다.member1 : teamAmember2 : teamAmember3 : teamA
-
해결됨입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기
테스트코드 실패
안녕하세요, 테스트코드 결과가 계속 실패되는데... 이유를 찾지 못하여 질문 드립니다. BeforeAll에서 projects를 디버그찍어 skills안의 skill확인 하면 제대로 들어간것이 보이는데, 테스트에 진입을 하면 skills가 0이 되는데...다른 변수들은 다 들어가고 skills만 0이 되는 상황입니다... 어디서 부터 실수를 만든건지 파악이 안되어 도움을 요청드립니다. 아래 현재 문제가 있는 코드 남깁니다.Git 저장소
-
미해결스프링 DB 2편 - 데이터 접근 활용 기술
REQUIRES_NEW와 내부 호출
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]내부 호출 문제점은 REQUIRES_NEW 어노테이션이 적용된 경우에도 동일하게 발생하는 것 같은데 맞을까요?? 아래 코드를 실행했을 때 커밋이 한 번도 발생하지 않는 것을 로그로 확인했습니다. 혹시나 해서 여쭤봅니다!! @Slf4j @SpringBootTest public class InternalCallV1Test { @Autowired CallService callService; @Test void externalCall() { callService.external(); } @TestConfiguration static class InternalCallV1TestConfig { @Bean CallService callService() { return new CallService(); } } @Slf4j static class CallService { @Transactional(propagation = Propagation.REQUIRES_NEW) public void internal() { log.info("call internal"); printTxInfo(); } @Transactional public void external() { log.info("call external"); printTxInfo(); this.internal(); throw new RuntimeException("종료"); } private void printTxInfo() { boolean txActive = TransactionSynchronizationManager.isActualTransactionActive(); log.info("tx active={}", txActive); } } }
-
미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
sequence 전략에서 여러 대의 서버가 동시에 번호를 요청하는 경우
sequence 전략에서 여러 대의 서버가 동시에 번호를 요청하는 경우 어떤 방식으로 동작할 지 궁금합니다.allocationSize로 설정한 값을 여러 대의 서버가 같이 공유해서 사용하고, 할당받은 시퀀스 값을 넘어선 번호를 요청한 서버가 DB에서 시퀀스 값을 할당받아 온다.접근하는 서버마다 allocationSize 만큼 할당을 받아서 각자 할당받은 번호를 사용한다. 제 생각에는 서버가 내려가면 사용하지 못한 시퀀스 값은 날라간다는 말씀을 듣고, 각 서버마다 allocationSize 만큼 할당을 받아서 사용할 것 같은데 정확히 어떻게 동작할 지 궁금합니다.
-
해결됨실전! 스프링 데이터 JPA
코드를 실행해도 DB에 데이터가 추가되지 않습니다
이 테스트를 실행하면 스프링 데이터 JPA의 save 메서드를 사용했으니 바로 DB에 member 하나가 추가되어야 할 것 같은데, DB를 보니 추가된 데이터가 없습니다.application.yml이 문제인가 해서 ddl-auto 설정을 보니 create로 잘 되어있고, 로그를 보면 sql문은 잘 날아가는데 왜 DB에 저장이 안 되는지를 도저히 모르겠어서 프로젝트 압축 파일을 남깁니다 ㅠㅠhttps://drive.google.com/file/d/1tyWEF3_l80u8Vban3No5q59y-x8sjWqF/view?usp=sharing테스트 코드들은 MemberRepositoryTest 클래스에다가 전부 넣어놨습니다. 감사합니다.
-
미해결Java/Spring 테스트를 추가하고 싶은 개발자들의 오답노트
도메인 객체 / entity 객체 / requset, response 객체 간 mapping 시 mapper 사용
안녕하세요. 관련 내용을 듣다가 궁금한 것이 생겨 질문드립니다.해당 강의를 듣다가 궁금한 것이 생겨 질문드립니다.찾아보니 DDD나 헥사고날 아키텍처에서 request / response 객체 <-> 도메인 객체 <-> entity 객체 간의 mapping이 일어나는 것을 볼 수 있는데요..이런 경우 controller, service, infral layer가 모두 mapper 관련 라이브러리 객체나 직접 구현한 mapper 객체를 들고 변환시켜주는 구조는 별로 좋지 못한 구조일까요?제 개인적인 의견으로는 결국 지금 구조에서 response / request 객체나 entitiy 객체가 domain 객체로 변환시켜주기위해 domain 객체에 의존성이 생기는 구조인데 mapper 객체를 쓰면 이런 객체간의 의존성을 최소화시킨다는 점에서 장점이 있을 것 같은데요.. 물론 정답은 없겠지만, 의견이 어떠신지 궁금하여 질문드립니다.
-
미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
시퀀스 전략의 allocation 문의
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]서비스가 시작 될때, hibernate가 DB와 connection을 한 뒤, 해당 시퀀스 name기준으로 allocationSize 만큼 미리 값을 갖고와 메모리에 캐싱한뒤에 사용한다는건 이해를 했습니다. 그렇다면, 요즘은 CI/CD Flow를 통해 자동화된 지속적인 통합/배포가 트랜드인 상황에서, 하루에도 여러번 서비스가 업데이트 되어 배포가 될 것이고, 이로 인하여 hibernate는 매번 DB에 새로운 connection을 진행할 텐데, allocationSize=50일 경우 오히려 자원 낭비가 더 심해지는게 아닌지 궁금합니다.
-
미해결Practical Testing: 실용적인 테스트 가이드
@Transactional 차이로 인해 재고의 quantity 가 감소되지 않는 이유에 대해 질문 드립니다.
안녕하세요, 강사님 테스트에 관심이 생겼고, 강사님 덕분에 테스트에 대해 하나하나 알아가는 재미를 느끼는 중입니다!좋은 강의 감사드립니다! 강의를 진행하던 도중 의문이 있어서 질문 드립니다. 문제 상황입니다.stock.deductQuantity(quantity) 부분에서 stock 의 this.quantity 가 파라미터로 들어온 quantity 만큼 갯수가 감소되는 것을 확인하였습니다. (아래 사진에 빨간줄로 밑줄 그었습니다) 그러나, 테스트의 결과는 실패로 떴습니다.그 이유는 감소된 재고의 수량(Stock 의 quantity)이 아래 사진처럼 테스트에 반영되지 않았습니다.해당 테스트 코드입니다. 강사님의 테스트 코드와 일치하게 짰습니다.@DisplayName("재고와 관련된 상품이 포함되어 있는 주문번호 리스트를 받아 주문을 생성한다.") @Test void createOrderWithStock() { // given Product product1 = createProduct(BOTTLE, "001", 1000); Product product2 = createProduct(BAKERY, "002", 3000); Product product3 = createProduct(HANDMADE, "003", 5000); productRepository.saveAll(List.of(product1, product2, product3)); Stock stock1 = Stock.create("001", 2); Stock stock2 = Stock.create("002", 2); stockRepository.saveAll(List.of(stock1, stock2)); OrderAddRequest request = OrderAddRequest.builder() .productNumbers(List.of("001", "001", "003", "002")) .build(); LocalDateTime registeredDateTime = LocalDateTime.now(); // when OrderResponse orderResponse = orderService.createOrder(request, registeredDateTime); // then assertThat(orderResponse.getId()).isNotNull(); assertThat(orderResponse) .extracting("registeredDateTime", "totalPrice") .contains(registeredDateTime, 10000); assertThat(orderResponse.getProducts()).hasSize(4) .extracting("productNumber", "price") .containsExactlyInAnyOrder( tuple("001", 1000), tuple("001", 1000), tuple("002", 3000), tuple("003", 5000) ); List<Stock> stocks = stockRepository.findAll(); assertThat(stocks).hasSize(2) .extracting("productNumber", "quantity") .containsExactlyInAnyOrder( tuple("001", 0), tuple("002", 1) ); } 왜 테스트가 실패하는지 한참 헤매다가 OrderService 에 @Transactional 을 추가하였더니 Stock의 감소된 quantity 가 테스트에 반영이 되었고, 테스트가 성공하게 되었습니다.// OrderService 중 일부 발췌 @Transactional @RequiredArgsConstructor @Service public class OrderService { private final OrderRepository orderRepository; private final ProductRepository productRepository; private final StockRepository stockRepository; 저의 얕은 지식으로는 @Transactional 이 왜 테스트에 영향을 주게 되었는지 이해가 도통되지 않아 강사님께 질문을 드립니다
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
MemberServiceTest 오류
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요.MemberServiceTest 하려고 하는데 처음부터 오류뜨고 안돼요Cannot resolve symbol 'junit', Cannot resolve symbol 'Test', Identifier or type expected 이렇게 오류가 3개가 뜹니다. @Test 자체도 안되네요.. 사진첨부 같이 하겠습니다!
-
미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
table not found 현상이 발생합니다.
기본 키 매핑 강의에서 @GeneratedValue의 전략 중 IDENTITY로 코드를 실행하니까데이터베이스에서 member 테이블 자체가 사라지고 인서트 쿼리도 안 나갑니다. 뭐가 문제인지 모르겠네요ㅠㅠ오류 코드는 다음과 같습니다.8월 21, 2024 7:27:28 오후 org.hibernate.jpa.internal.util.LogHelper logPersistenceUnitInformationINFO: HHH000204: Processing PersistenceUnitInfo [name: hello]8월 21, 2024 7:27:28 오후 org.hibernate.Version logVersionINFO: HHH000412: Hibernate ORM core version 6.4.2.Final8월 21, 2024 7:27:28 오후 org.hibernate.cache.internal.RegionFactoryInitiator initiateServiceINFO: HHH000026: Second-level cache disabled8월 21, 2024 7:27:29 오후 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configureWARN: HHH10001002: Using built-in connection pool (not intended for production use)8월 21, 2024 7:27:29 오후 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreatorINFO: HHH10001005: Loaded JDBC driver class: org.h2.Driver8월 21, 2024 7:27:29 오후 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreatorINFO: HHH10001012: Connecting with JDBC URL [jdbc:h2:tcp://localhost/~/test]8월 21, 2024 7:27:29 오후 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreatorINFO: HHH10001001: Connection properties: {password=****, user=sa}8월 21, 2024 7:27:29 오후 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreatorINFO: HHH10001003: Autocommit mode: false8월 21, 2024 7:27:29 오후 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl$PooledConnections <init>INFO: HHH10001115: Connection pool size: 20 (min=1)8월 21, 2024 7:27:29 오후 org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl constructDialectWARN: HHH90000025: H2Dialect does not need to be specified explicitly using 'hibernate.dialect' (remove the property setting and it will be selected by default)8월 21, 2024 7:27:30 오후 org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator initiateServiceINFO: HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)Hibernate:drop table if exists Member cascade8월 21, 2024 7:27:30 오후 org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl getIsolatedConnectionINFO: HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@3902bd2c] for (non-JTA) DDL execution was not in auto-commit mode; the Connection 'local transaction' will be committed and the Connection will be set into auto-commit mode.Hibernate:create table Member (id varchar(255) generated by default as identity,name varchar(255) not null,primary key (id))8월 21, 2024 7:27:31 오후 org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl getIsolatedConnectionINFO: HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@6ee99964] for (non-JTA) DDL execution was not in auto-commit mode; the Connection 'local transaction' will be committed and the Connection will be set into auto-commit mode.8월 21, 2024 7:27:31 오후 org.hibernate.tool.schema.internal.ExceptionHandlerLoggedImpl handleExceptionWARN: GenerationTarget encountered exception accepting command : Error executing DDL "create table Member (id varchar(255) generated by default as identity,name varchar(255) not null,primary key (id))" via JDBC [Feature not supported: "CHARACTER VARYING(255)";]org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL "create table Member (id varchar(255) generated by default as identity,name varchar(255) not null,primary key (id))" via JDBC [Feature not supported: "CHARACTER VARYING(255)";]at org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.accept(GenerationTargetToDatabase.java:94)at org.hibernate.tool.schema.internal.Helper.applySqlString(Helper.java:233)at org.hibernate.tool.schema.internal.Helper.applySqlStrings(Helper.java:217)at org.hibernate.tool.schema.internal.SchemaCreatorImpl.createTables(SchemaCreatorImpl.java:420)at org.hibernate.tool.schema.internal.SchemaCreatorImpl.createSequencesTablesConstraints(SchemaCreatorImpl.java:340)at org.hibernate.tool.schema.internal.SchemaCreatorImpl.createFromMetadata(SchemaCreatorImpl.java:239)at org.hibernate.tool.schema.internal.SchemaCreatorImpl.performCreation(SchemaCreatorImpl.java:172)at org.hibernate.tool.schema.internal.SchemaCreatorImpl.doCreation(SchemaCreatorImpl.java:142)at org.hibernate.tool.schema.internal.SchemaCreatorImpl.doCreation(SchemaCreatorImpl.java:118)at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:256)at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.lambda$process$5(SchemaManagementToolCoordinator.java:145)at java.base/java.util.HashMap.forEach(HashMap.java:1421)at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:142)at org.hibernate.boot.internal.SessionFactoryObserverForSchemaExport.sessionFactoryCreated(SessionFactoryObserverForSchemaExport.java:37)at org.hibernate.internal.SessionFactoryObserverChain.sessionFactoryCreated(SessionFactoryObserverChain.java:35)at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:315)at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:450)at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1507)at org.hibernate.jpa.HibernatePersistenceProvider.createEntityManagerFactory(HibernatePersistenceProvider.java:55)at jakarta.persistence.Persistence.createEntityManagerFactory(Persistence.java:80)at jakarta.persistence.Persistence.createEntityManagerFactory(Persistence.java:55)at hellojpa.JpaMain.main(JpaMain.java:9)Caused by: org.h2.jdbc.JdbcSQLFeatureNotSupportedException: Feature not supported: "CHARACTER VARYING(255)"; SQL statement:create table Member (id varchar(255) generated by default as identity,name varchar(255) not null,[50100-224]at org.h2.message.DbException.getJdbcSQLException(DbException.java:568)at org.h2.message.DbException.getJdbcSQLException(DbException.java:489)at org.h2.message.DbException.get(DbException.java:223)at org.h2.message.DbException.get(DbException.java:199)at org.h2.message.DbException.getUnsupportedException(DbException.java:287)at org.h2.command.ddl.SequenceOptions.getBounds(SequenceOptions.java:316)at org.h2.command.ddl.SequenceOptions.getBounds(SequenceOptions.java:244)at org.h2.schema.Sequence.<init>(Sequence.java:100)at org.h2.table.Column.initializeSequence(Column.java:459)at org.h2.command.ddl.CommandWithColumns.generateSequences(CommandWithColumns.java:103)at org.h2.command.ddl.CreateTable.update(CreateTable.java:113)at org.h2.command.CommandContainer.update(CommandContainer.java:169)at org.h2.command.Command.executeUpdate(Command.java:256)at org.h2.server.TcpServerThread.process(TcpServerThread.java:413)at org.h2.server.TcpServerThread.run(TcpServerThread.java:191)at java.base/java.lang.Thread.run(Thread.java:833)at org.h2.message.DbException.getJdbcSQLException(DbException.java:568)at org.h2.engine.SessionRemote.readException(SessionRemote.java:650)at org.h2.engine.SessionRemote.done(SessionRemote.java:619)at org.h2.command.CommandRemote.executeUpdate(CommandRemote.java:237)at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:262)at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:231)at org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.accept(GenerationTargetToDatabase.java:80)... 21 moreHibernate:/* insert forhellojpa.Member */insertintoMember (name, id)values(?, default)8월 21, 2024 7:27:31 오후 org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptionsWARN: SQL Error: 42104, SQLState: 42S048월 21, 2024 7:27:31 오후 org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptionsERROR: Table "MEMBER" not found (this database is empty); SQL statement:/* insert for hellojpa.Member */insert into Member (name,id) values (?,default) [42104-224]8월 21, 2024 7:27:31 오후 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl$PoolState stopINFO: HHH10001008: Cleaning up connection pool [jdbc:h2:tcp://localhost/~/test]Process finished with exit code 0
-
해결됨실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
양방향 @OneToOne 조회에 대해 질문 있습니다.
@Entity public class Delivery { @Id @Column(name = "delivery_id") @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @OneToOne(fetch = FetchType.LAZY, mappedBy = "delivery") private Order order; private Address address; @Enumerated(EnumType.STRING) private DeliveryStatus status; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Order getOrder() { return order; } public void setOrder(Order order) { this.order = order; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public DeliveryStatus getStatus() { return status; } public void setStatus(DeliveryStatus status) { this.status = status; } }@Entity @Table(name = "orders") public class Order { @Id @Column(name = "order_id") @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "delivery_id") private Delivery delivery; private LocalDateTime orderDate; @Enumerated(EnumType.STRING) private OrderStatus status; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Delivery getDelivery() { return delivery; } public void setDelivery(Delivery delivery) { this.delivery = delivery; } public LocalDateTime getOrderDate() { return orderDate; } public void setOrderDate(LocalDateTime orderDate) { this.orderDate = orderDate; } public OrderStatus getStatus() { return status; } public void setStatus(OrderStatus status) { this.status = status; } }public class Test2 { public static void main(String[] args) { EntityManagerFactory emf = Persistence.createEntityManagerFactory("my-pu"); EntityManager em = emf.createEntityManager(); EntityTransaction tx = em.getTransaction(); try { tx.begin(); Delivery delivery = new Delivery(); delivery.setStatus(DeliveryStatus.READY); delivery.setAddress(new Address("city", "street", "zipcode")); em.persist(delivery); Order order = new Order(); order.setOrderDate(LocalDateTime.now()); order.setStatus(OrderStatus.ORDER); order.setDelivery(delivery); em.persist(order); // Order 에 fetch = FetchType.LAZY 설정 , delivery 실제 값 사용 { em.flush(); em.clear(); Order findOrder = em.find(Order.class, order.getId()); Delivery findDelivery = findOrder.getDelivery(); System.out.println("findDelivery : " + findDelivery.getStatus()); } tx.commit(); }finally { em.close(); } } }실행결과Hibernate: /* insert for com.mycom.myapp.ex.Delivery */insert into Delivery (city, street, zipcode, status) values (?, ?, ?, ?) Hibernate: /* insert for com.mycom.myapp.ex.Order */insert into orders (delivery_id, orderDate, status) values (?, ?, ?) Hibernate: select o1_0.order_id, o1_0.delivery_id, o1_0.orderDate, o1_0.status from orders o1_0 where o1_0.order_id=? Hibernate: select d1_0.delivery_id, d1_0.city, d1_0.street, d1_0.zipcode, d1_0.status from Delivery d1_0 where d1_0.delivery_id=? Hibernate: select o1_0.order_id, o1_0.delivery_id, o1_0.orderDate, o1_0.status from orders o1_0 where o1_0.delivery_id=? findDelivery : READY 강의에서 진행한 Order와 Delivery를 가지고 조회 테스트를 해봤습니다. 우선 Order와 Delivery를 양방향으로 OneToOne 관계를 설정했습니다. 그리고 둘다 각각 지연로딩으로 설정했습니다.em.flush(); em.clear(); Order findOrder = em.find(Order.class, order.getId()); Delivery findDelivery = findOrder.getDelivery(); System.out.println("findDelivery : " + findDelivery.getStatus());그리고 이 부분에 대해 아래와 같이 이해를 했습니다.맨처음 em.find()를 해서 Order에 대해 DB에 select문을 보냅니다. 이때 delivery와는 지연로딩이므로 추가 select가 발생하지 않습니다. 그리고 findDelivery.getStatus()); 에서 delivery의 실제 값을 사용하므로 이때 delviery에 대한 select문이 발생합니다. 정리하면 처음에는 order에 대한 select문 그리고 실제 delevery 값을 사용할때 delivery에 대한 select문 해서 총 두번의 select문이 발생할꺼라고 예상을 했습니다.근데 실제 실행결과를 보니 총 세번의 select문이 발생했습니다. 실행결과에서 첫번째 select문과 두번째 select문이 출력된거는 이해가 되는데 마지막 select문은 왜 발생했는지 모르겠습니다.
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
MemberRepositoryTest 실행함에 있어 오류
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]application.ymlspring: datasource: url: jdbc:h2:tcp://localhost/~/jpashop username: sa password: driver-class-name: org.h2.Driver jpa: hibernate: ddl-auto: create properties: hibernate: # show-sql: true format_sql: true logging.level: org.hibernate.SQL: debug # org.hibernate.type:trace org.hibernate.orm.jdbc.bind: tracebuild.gradleplugins { id 'java' id 'org.springframework.boot' version '3.2.0' id 'io.spring.dependency-management' version '1.1.4' } 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 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.9.0' compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.h2database:h2' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' //JUnit4 추가 testImplementation("org.junit.vintage:junit-vintage-engine") { } test { useJUnitPlatform() }MemberRepositoryTest.javapackage jpabook.jpashop; import jpabook.jpashop.Entity.Member; import jpabook.jpashop.Repository.MemberRepository; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.Rollback; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.annotation.Transactional; import static org.assertj.core.api.Assertions.assertThat; @ExtendWith(SpringExtension.class) @SpringBootTest class MemberRepositoryTest { @Autowired MemberRepository memberRepository; @Test @Transactional @Rollback(false) public void testMember() throws Exception { //given Member member = new Member(); member.setUsername("memberA"); //when Long savedId = memberRepository.save(member); Member findMember = memberRepository.find(savedId); //then assertThat(findMember.getId()).isEqualTo(member.getId()); assertThat(findMember.getUsername()).isEqualTo(member.getUsername()); System.out.println("findMember == member: " + (findMember == member)); } }테스트 실행 시 No matching tests found in any candidate test task. Requested tests: Test pattern jpabook.jpashop.Repository.MemberRepositoryTest in task :test* Try:> Run with --stacktrace option to get the stack trace.> Run with --info or --debug option to get more log output.> Run with --scan to get full insights.> Get more help at https://help.gradle.org.Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.For more on this, please refer to https://docs.gradle.org/8.8/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.BUILD FAILED in 1s4 actionable tasks: 1 executed, 3 up-to-date오류가 발생합니다.
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
order.cancel();의 더티체킹에 대해 질문 있습니다.
@Entity @Getter @Setter @DiscriminatorColumn(name = "dtype") @Inheritance(strategy = InheritanceType.SINGLE_TABLE) public abstract class Item { @Id @Column(name = "item_id") @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private int price; private int stockQuantity; @ManyToMany(mappedBy = "items") private List<Category> categories = new ArrayList<>(); //== 비즈니스 로직 ==// public void addStock(int quantity) { this.stockQuantity += quantity; } public void removeStock(int quantity) { int restStock = this.stockQuantity - quantity; if (restStock < 0) { throw new NotEnoughStockException("need more stock"); } this.stockQuantity = restStock; } }@Entity @Getter @Setter @NoArgsConstructor(access = AccessLevel.PROTECTED) public class OrderItem { @Id @Column(name = "order_item_id") @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "order_id") private Order order; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "team_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() { getItem().addStock(count); } //==조회 로직==// public int getTotalPrice() { return getOrderPrice() * getCount(); } }@Entity @Getter @Setter @Table(name = "orders") @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Order { @Id @Column(name = "order_id") @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "member_id") private Member member; @OneToMany(mappedBy = "order", cascade = CascadeType.ALL) private List<OrderItem> orderItems = new ArrayList<>(); @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumn(name = "delivery_id") private Delivery delivery; private LocalDateTime orderDate; @Enumerated(EnumType.STRING) private OrderStatus status; //== 연관관계 메서드 ==// public void addMember(Member member) { this.member = member; member.getOrders().add(this); } public void addOrderItem(OrderItem orderItem) { orderItems.add(orderItem); orderItem.setOrder(this); } public void addDelivery(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.setStatus(OrderStatus.ORDER); order.setOrderDate(LocalDateTime.now()); return order; } //==비즈니스 로직==// /* * 주문 취소 * */ public void cancel() { if (delivery.getStatus() == DeliveryStatus.COMP) { throw new IllegalStateException("이미 배송 완료된 상품은 취소가 불가능합니다."); } this.setStatus(OrderStatus.CANCEL); for (OrderItem orderItem : orderItems) { orderItem.cancel(); } } //==조회 로직==// /* * 전체 주문 가격 조회 * */ public int getTotalPrice() { int totalPrice = 0; for (OrderItem orderItem : orderItems) { totalPrice += orderItem.getTotalPrice(); } return totalPrice; } }@Service @Transactional(readOnly = true) @RequiredArgsConstructor public class OrderService { private final OrderRepository orderRepository; private final MemberRepository memberRepository; private final ItemRepository itemRepository; // 주문 @Transactional public Long order(Long memberId, Long itemId, int count) { // 엔티티 조회 Member member = memberRepository.findOne(memberId); Item item = itemRepository.findOne(itemId); // 배송정보 생성 Delivery delivery = new Delivery(); delivery.setAddress(member.getAddress()); // 주문상품 생성 OrderItem orderItem = OrderItem.createOrderItem(item, item.getPrice(), count); // 주문 생성 Order order = Order.createOrder(member, delivery, orderItem); // 주문 저장 orderRepository.save(order); return order.getId(); } // 취소 @Transactional public void cancelOrder(Long orderId) { Order order = orderRepository.findOne(orderId); order.cancel(); } // 검색 // public List<Order> findOrders() { // return orderRepository.f // } }OrderService에서 cancelOrder()을 할 경우 @Transactional에 의해 자동으로 트랜잭션을 시작하고 커밋을 보내고 커밋을 보낼때 플러시가 발생하고 더티체킹이 발생해 직업 update를 안해도 알아서 update 쿼리가 날라가는 부분에 대해서는 이해를 했습니다.궁금한 부분은 현재 Order order = orderRepository.findOne(orderId); 를 통해 order 엔티티만 조회 했으니 영속성 컨텍스트에는 order 엔티티만 존재하므로 Item 엔티티는 영속성 컨텍스트에 없는데 어떻게 더티체킹에 들어갈까가 저의 의문입니다.이 부분에 대해서 아래와 같이 생각해봤는데 맞게 생각한건지 궁금합니다. 잘못된 부분에 대해서 알려주시면 감사하겠습니다.order.cancel();의 cancel() 메서드를 보면 orderItem.cancel(); 부분이 존재한다.orderItem.cancel() 의 cancel() 메서드를 보면 getItem().addStock(count); 부분이 존재한다.여기서 getItem()을 사용해서 (현재 Order와 OrderItem은 지연로딩 관계) 실제 orderItem 값을 사용하므로 DB에서 orderItem 엔티티를 조회해서 영속성 컨텍스트에 저장한다.다시 orderItem.cancel() 의 cancel() 메서드에서 addStock(count) 메서드를 들어가 보면 Item의 addStock() 메서드로 이동한다. 여기서 this.stockQuantity += quantity; 를 통해 Item의 실제 값을 사용하므로 Item 엔티티도 DB에서 조회해서 영속성 컨텍스트에 저장한다.결과적으로 OrderService의 cancelOrder() 메서드에서 order.cancel();가 호출되면 Item과 OrderItem 엔티티도 영속성 컨텍스트에 저장되서 더티체킹 범위에 들어가게 된다.