월 17,600원
5개월 할부 시다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결실전! 스프링 데이터 JPA
test code에서 repository의 값을 읽어오지 못합니다.
안녕하세요. 강의 잘 듣고 있습니다. tdd를 작성 중 계속 findByStoreId, findByMemberId가 다 되지 않고 있어 그 이유가 궁금해 질문 남깁니다. executorService를 사용하지 않고 orderService.postOrder을 사용하면 findByStoreId, findByMemberId가 정상적으로 실행이 되는데 executorService를 사용하기만 하면 계속 repo에 있는 값을 찾아오지 못하네요,, ㅠ 코드는 아래 첨부해놓았습니다!public interface MemberRepository extends JpaRepository<Member, Long>, MemberRepositoryCustom { Optional<Member> findByMemberId(Long id); }public class Member extends Timestamped { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long memberId; @Column(nullable = false) private String name; @Column(nullable = false) private String nickname; @Column(nullable = false) private String password; @Column(nullable = false) private String address; @Column(nullable = false) private String role; @JsonIgnore @OneToMany(fetch = FetchType.LAZY, mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true ) private List<Orders> orders = new ArrayList<>(); @JsonManagedReference @OneToMany(fetch = FetchType.LAZY, mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true ) private List<Likes> likes = new ArrayList<>(); @OneToMany(fetch = FetchType.LAZY, mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true ) private List<Store> stores = new ArrayList<>(); }public class Item extends Timestamped{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long itemId; @Column(nullable = false) private String name; @Column(nullable = false) private int amount; @Column(nullable = false) private int price; //@Enumerated(EnumType.STRING) private String category; @JsonIgnore @OneToMany(fetch = FetchType.LAZY, mappedBy = "item", cascade = CascadeType.ALL, orphanRemoval = true ) private List<OrderHasItem> orderHasItems = new ArrayList<>(); @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name="store_id", nullable = true) private Store store; public Integer changeStock(int amount){ //case1) this.amount < amount if (this.amount < amount){ throw new CustomException(ErrorCode.NOT_ENOUGH_STOCK); } else if (this.amount == 0){ throw new CustomException(ErrorCode.OUT_OF_STOCK); } else{ this.amount -= amount; //stock 감소 } return this.amount; }@Transactional(rollbackFor = {CustomException.class}) public ResponseDto<?> postOrder(OrderRequestDto requestDto, HttpServletRequest request) { //case1)case2)token validity check tokenProvider.tokenValidationCheck(request); //case3) memberService.isPresentMember(requestDto.getMemberId()); //case4)StoreId에 해당하는 가게가 존재하지 않을 때 if (!storeRepository.existsById(requestDto.getStoreId())) { throw new CustomException(ErrorCode.STORE_NOT_FOUND); } //case5)ItemId에 해당하는 Item이 존재하지 않을 때 requestDto.getItemId().forEach(itemId -> { if (!itemRepository.existsById(itemId)) { throw new CustomException(ErrorCode.ITEM_NOT_FOUND); } }); //case6)Item 주문 수량이 0일 때 if (requestDto.getItemId().size() == 0) { throw new CustomException(ErrorCode.NEED_OVER_ONE); } //itemId에 해당하는 내용들을 찾아서 리스트로 List<Item> itemList = requestDto.getItemId() .stream() .map(item -> itemRepository.findByItemId(item).orElse(null)) //x-Lock(pessimistic_write) .collect(toList()); //item을 OrderHasItems에 넣어두기 Orders order = Orders.builder() .member(memberRepository.findByMemberId(requestDto.getMemberId()).orElse(null)) //s-Lock .store(storeRepository.findByStoreId(requestDto.getStoreId()).orElse(null)) //s-Lock .build(); orderRepository.save(order); //s-Lock for (int i=0; i<requestDto.getItemId().size(); i++){ Item item = itemList.get(i); Integer amount = requestDto.getAmount().get(i); OrderHasItem item1 = OrderHasItem.builder() .orders(order) .item(item) .amount(amount) .build(); orderHasItemRepository.save(item1); //s-Lock //stock update item.changeStock(amount); }@Transactional @SpringBootTest public class ServiceCustom{ @Autowired MemberService memberService; @Autowired OrderService orderService; @Autowired LikeService likeService; @Autowired SearchService searchService; @Autowired StoreService storeService; @Autowired MemberRepository memberRepository; @Autowired OrderRepository orderRepository; @Autowired OrderHasItemRepository orderHasItemRepository; @Autowired LikeRepository likeRepository; @Autowired ItemRepository itemRepository; @Autowired StoreRepository storeRepository; @Autowired RefreshTokenRepository refreshTokenRepository; @Autowired TokenProvider tokenProvider; @BeforeEach public void beforeEach() { //FK check 후 삭제 orderHasItemRepository.deleteAll(); likeRepository.deleteAll(); storeRepository.deleteAll(); itemRepository.deleteAll(); orderRepository.deleteAll(); refreshTokenRepository.deleteAll(); memberRepository.deleteAll(); } @AfterEach public void afterEach() { //FK check 후 삭제 orderHasItemRepository.deleteAll(); likeRepository.deleteAll(); storeRepository.deleteAll(); itemRepository.deleteAll(); refreshTokenRepository.deleteAll(); orderRepository.deleteAll(); memberRepository.deleteAll(); } //create Entity protected Store createStore(String name, String address, String category, Long memberId) { Store store = Store.builder() .name(name) .address(address) .category(category) .member(memberRepository.findByMemberId(memberId).get()) .build(); storeRepository.save(store); return store; } protected Item createItem(String name, int amount, int price, String category, Long storeId) { Item item = Item.builder() .name(name) .amount(amount) .store(storeRepository.findByStoreId(storeId).get()) .price(price) .category(category) .build(); itemRepository.save(item); return item; } protected Member createMember(String name, String nickname, String password, String address, String role) { Member member = Member.builder() .name(name) .nickname(nickname) .password(password) .address(address) .role(role) .build(); memberRepository.save(member); return member; } //create Dto protected MemberRequestDto createMemberRequestDto(String name, String nickname, String password, String address, String role) { return MemberRequestDto.builder() .name(name) .nickname(nickname) .password(password) .address(address) .role(role) .build(); } protected OrderRequestDto createOrderRequestDto(Long storeId, Long item1, Integer amount1, Long memberId) { List<Long> itemId = new ArrayList<>(); itemId.add(item1); List<Integer> amount = new ArrayList<>(); amount.add(amount1); return OrderRequestDto.builder() .storeId(storeId) .itemId(itemId) .amount(amount) .memberId(memberId) .build(); } @Test @DisplayName("주문 요청 동시에 진행할 시 동시성 issue 확인") public void 주문_요청_동시성_문제_테스트() { //given Member member = memberRepository.save(createMember("member1", "member1", "1234", "address1", "CONSUMER")); Store store = storeRepository.save(createStore( "store1","address1","category1",member.getMemberId())); Item item = itemRepository.save(createItem( "item1", 1000, 8000,"category1",store.getStoreId())); OrderRequestDto order = createOrderRequestDto(store.getStoreId(), item.getItemId(), 1, member.getMemberId()); MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader("Authorization", "Bearer " + "1234567890"); request.addHeader("Refresh-Token", "1234567890"); int amountBefore = itemRepository.findAmountByItemId(item.getItemId()); //when int threadCount = 2; ExecutorService executorService = Executors.newFixedThreadPool(threadCount); //concurrent : 2 CountDownLatch latch = new CountDownLatch(threadCount); for (int i=0; i<2; i++){ executorService.submit(() -> { try{ orderService.postOrder(order, request); }catch (Exception e) { log.info("exception : {}", e.getMessage()); }finally{ latch.countDown(); } }); } //then int amountAfter = itemRepository.findAmountByItemId(item.getItemId()); assertThat(amountAfter-amountBefore).isNotEqualTo(2); }
- 해결됨실전! 스프링 데이터 JPA
유저를 차단할때 유저 인덱스2개를 동시에 못가져오나요 ?
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]유저를 차단할때 유저 테이블과 블럭 테이블이 있는 경우 유저가 유저를 차단할때 차단 테이블엔 @Builder @Getter @NoArgsConstructor @AllArgsConstructor @Entity public class Block extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "block_idx") @Comment("차단 인덱스") private Long idx; @ManyToOne(fetch = LAZY) @JoinColumn(name = "member_idx") @Comment("차단 하려는 유저 인덱스") private Member member; @ManyToOne(fetch = LAZY) @JoinColumn(name = "member_idx") @Comment("차단 당하는 유저 인덱스") private Member blockMember; }부모 인덱스 2개를 못가져오나요 ?이럴경우에는 하나만 fk로 묶고 하나는개념적으로 외래키를 써야하나요 ?
- 미해결실전! 스프링 데이터 JPA
벌크 수정과 영속성 컨텍스트 초기화 관련 문의
이전 강의부터 궁금했던건데, 해결되지 않아 문의 드립니다.벌크 수정을 했을때, DB 값과 1차캐시에 저장된 값이 달라지는 경우 flush 해 주면 메모리의 값은 초기화 되어 이후 쿼리는 DB 로 다시 요청 하는걸로 이해하고 있습니다.궁금한 점은 만약 시스템이 다르면 어떻게 될까요?벌크 수정을 batch 장비에서 돌리고 flush 해 준다고 해도, 서비스 장비에 올려진 값들은 계속 유지될것 같습니다.벌크 작업을 스케쥴링해서 서비스 장비도 함께 재시작 해주거나 하면 될것 같지만, 혹시 라이브 중에 벌크 작업을 해 줘야 할경우엔 다른 장비의 값들은 어떻게 초기화 해줘야 할지 궁금합니다.
- 미해결실전! 스프링 데이터 JPA
새로운 엔티티가 기본 키 직접 할당 전략을 사용하는 경우 변경 감지로 새로운 엔티티를 추가할 때 merge가 발생하는지?
새로운 엔티티를 구별하는 방법에 대해 강의를 듣다가 기존 영속성 엔티티에 비영속성 엔티티를 추가하고 CascadeType.ALL을 통해 비영속성 엔티티를 저장할 때비영속성 엔티티가 기본 키를 직접 할당하는 전략을 사용하면 의도하지 않은 merge가 발생할 것 같아서 테스트를 진행하고 있습니다. 추가적으로 .. Team 엔티티를 저장한 이후 새로운 Member 엔티티를 추가하는 과정에서 @Rollback(value=false)를 주지않으면 Member 엔티티의 insert문이 발생하지 않아서 이 부분도 추가적으로 궁금합니다. @ToString @Getter @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Team { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @OneToMany(mappedBy = "team", cascade = CascadeType.ALL) private List<Member> members = new ArrayList<>(); private String name; public void addMember(Member member) { member.setTeam(this); members.add(member); } @Builder private Team(String name) { this.name = name; } } @ToString(exclude = "team") @Getter @Setter @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Member extends BaseEntity{ @Id private Long id; private String username; private Integer age; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "team_id") private Team team; @Builder private Member(Long id,String username, Integer age, Team team) { this.id = id; this.username = username; this.age = age; this.team = team; } } 테스트 코드는 다음과 같습니다. @SpringBootTest class TeamJpaRepositoryTest { @Autowired private TeamJpaRepository teamJpaRepository; @Autowired private EntityManager em; @BeforeEach void beforeEach() { Team aTeam = Team.builder() .name("A Team") .build(); teamJpaRepository.save(aTeam); em.clear(); } @Test @Transactional public void saveMember() throws Exception { //given Member userA = Member.builder() .id(10L) .age(10) .username("user A") .build(); //when Team savedTeam = teamJpaRepository.findByName("A Team"); savedTeam.addMember(userA); //then } } 왜 savedTeam에 대해 변경 감지에 대해 @Rollback(value=fasle) 어노테이션이 없으면 Member insert문이 발생하지 않는지 궁금합니다.스프링 데이터 JPA의 쿼리 메소드의 구현체가 @Transaction(readOnly = true)라고 해도 최초 트랜잭션이 readOnly 옵션이 false이기 때문에 트랜잭션 전파의 문제는 아닌 것 같아서요.@Rollback 옵션을 주었을 때 로그는 다음과 같습니다. 처음에 궁금했던 Team 엔티티에 새로운 Member 엔티티를 직접 기본 키를 할당하여 저장할 때해당 로그를 보면 merge가 발생하지 않는 것 같습니다.Persistable 인터페이스의 isNew 메소드를 오버 라이드하지 않았는데 말이죠.이 부분에 대해서 궁금합니다.plugins { id 'java' id 'org.springframework.boot' version '2.7.5' id 'io.spring.dependency-management' version '1.0.15.RELEASE' }
- 미해결실전! 스프링 데이터 JPA
delete 쿼리의 join
제가 delete쿼리를 join해서 사용하고 싶은데제가 시도했을 때는 jpql은 join도 불가하고 @EntityGraph로도 안되더라고요 그럼 delete시에는 다른 테이블과 join하는 방법이 없나요??(jpql이나 스프링 데이터 jpa를 사용시에)
- 해결됨실전! 스프링 데이터 JPA
양방향 연관관계에서 관계를 바꿀 때
[질문 내용]안녕하세요. 예전 부터 궁금했던 내용인데, 혹시 나중에 나오지 않을까 해서 미루다가 결국 질문합니다..! public void changeTeam(Team team){ this.team = team; team.getMembers().add(this); }예전 강의에서는 양방향 연관 관계에서 주인이 관계를 변경할 때 위처럼 Member Entity는 자신의 필드에 새로운 Team을 세팅하고 Team은 Member를 list에 추가하는 식으로만 마무리했던 것으로 기억합니다. 지나가는 말로 영한님께서 기존 Member와 연관 관계에 있던 Team list에서 Member를 remove하는 작업도 해야 하는데, 연습하는 거니까 빼셨다고 하셨던 것 같습니다. 이 부분이 조금 궁금했습니다.관계가 바뀌기 이전의 기존 Member의 Team list에서 Member를 지워 주는 부분을 어떤식으로 작성해야 할 지 조금 감이 안 잡힙니다..ㅠㅠ고려할 사항이 너무나도 많다고 해야 할까요.. public void changeTeam(Team team){ this.team.getMembers().remove(this); this.team = team; team.getMembers().add(this); }위처럼 단순히 remove()를 사용하기에는 몇 가지 고려할 사항들이 있었습니다.첫 번째 문제는 remove()의 경우 add와 달리 list의 Member들을 필요로 하는 로직이 들어 있기 때문에 DB로부터 Team의 Member list 정보를 불러온 다음에 동작한다는 점입니다.특정 로직은 Team의 Members를 사용할 일이 없어 굳이 DB와 Entity그래프를 일치시킬 필요가 없는데, remove()로직이 들어가면서 DB를 조회하는 일이 생긴다는 점입니다. 이로 인한 성능상 문제는 정말 미세하겠지만, 뭔가 조금 걸리는 느낌입니다.. 두 번째는 remove()를 할 때에 eqauls()를 사용한다는 점입니다.단순히 equals()를 오버라이딩하여 구현하면 될 줄 알았지만, 이 부분도 고려할 부분이 생각 보다 많았습니다.크게 세 가지로 나뉘는 것 같았습니다.pk를 이용한 equals()오버라이딩pk와 연관관계 필드를 제외한 필드로 eqauls()를 오버라이딩Business-Id를 이용한 eqauls()오버라이딩이렇게 세 가지 사항 정도가 고려되는 것 같았습니다.각각의 장단점이 있어 보였는데, 3번이 제일 괜찮은 방식으로 보였습니다.1번은 pk가 GenerateValue방식일 경우 Entity가 persist되기 이전에는 pk를 초기화하지 못 해, set과 같은 Collection을 사용할 때 제한이 생긴다는 점이나 NPE발생 가능성 내재, pk값이 null인 객체가 같은 객체로 인식이 되는 위험, 비영속 상태 객체와 영속 상태 객체의 eqauls연산 시 일치 불가 등이 있는 것 같았습니다. 사실 위의 문제점들이 발생할 만한 로직을 실제 작성하게 될 일이 많지는 않을 것 같지만 뭔가 내재된 위험이 많아 보여 패스했습니다.2번은 pk와 연관 관계 필드를 제외한 모든 필드들이 합쳐서 Unique한 값을 갖지 못할 경우 중복이 발생하는 문제점이 있어 보였습니다.3번이 제일 적절해 보였지만 Business-Id로 사용할 만한 데이터가 없을 경우 문제가 있을 것 같았습니다.특정 글에서는 UUID와 같이 Business-Id를 일부로 두기도 한다는 것 같은데 괜찮은 방식인 지는 모르겠습니다. 내용이 길어졌는데 결론은 양방향 연관 관계에서 관계를 바꿔 줄 때 Entity 그래프를 일치시키기 위해서 어떤 형태로 로직을 작성하는 지 궁금하다는 것입니다.이렇게 보니 제가 너무 이상한 방식으로 접근한 게 아닌가 생각이 듭니다ㅠㅠ 현업에서는 어떤 식으로 구현하는 지 궁금한데 혼자 공부하다 보니 마땅히 예시를 볼만한 곳이 없어 질문드립니다. 긴 글 읽어 주셔서 감사합니다..! (_ _)
- 미해결실전! 스프링 데이터 JPA
연관관계를 갖고 있는 entity에서 persist 호출시 연관관계의 entity 에도 insert 쿼리문이 실행 됩니다.
안녕하세요 사용자정의 리포지토리 구현 챕터를 듣고 실제 구현 후 이슈가 생겨서 문의를 드립니다!엔티티회사정보회사코드정보연관관계회사정보 1 : N 회사코드정보사용자정의 리포지토리 구성회사코드 리포지토리회사코드 사용자정의 리포지토리→ CompCodeInfo persist 호출하는 createCompCode method서비스 구성단순히 createCompCode 호출dto > entity 변환 static method→ CompInfo entity 내에 id 세팅 static method 호출 후 CompCodeInfo의 compInfo 세팅서비스 실행 결과궁긍한점JpaRepository에서 지원하는 save method로 CompCodeInfo 생성시 CompInfo에 대해 insert 문이 실행되지 않습니다.근데 위와 같은 구성으로 persist를 직접 호출시 CompInfo에 대해 insert문이 실행됩니다.save method도 결국 EntityManager의 persist를 호출하는건데 왜 이런 차이가 있는지 궁금합니다.
- 미해결실전! 스프링 데이터 JPA
MemberRepositoryTest 실행시 Exception
이전 버전의 강의에서도 IllegalStateException이 발생했는데 새 버전으로 따라해도 같은 Exception이 발생하네요ㅠㅠH2 데이터베이스는 연결해놓은 상태입니다. DB는 비어있습니다.Error Code: java.lang.IllegalStateException: Failed to load ApplicationContext at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:98) at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:124) at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:190) at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:132) at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:248) at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:138) at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$8(ClassBasedTestDescriptor.java:363) at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.executeAndMaskThrowable(ClassBasedTestDescriptor.java:368) at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$9(ClassBasedTestDescriptor.java:363) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179) at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625) at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:310) at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:735) at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:734) at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:762) at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeTestInstancePostProcessors(ClassBasedTestDescriptor.java:362) at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$instantiateAndPostProcessTestInstance$6(ClassBasedTestDescriptor.java:283) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateAndPostProcessTestInstance(ClassBasedTestDescriptor.java:282) at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$4(ClassBasedTestDescriptor.java:272) at java.base/java.util.Optional.orElseGet(Optional.java:364) at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$5(ClassBasedTestDescriptor.java:271) at org.junit.jupiter.engine.execution.TestInstancesProvider.getTestInstances(TestInstancesProvider.java:31) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$prepare$0(TestMethodTestDescriptor.java:102) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:101) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:66) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare$2(NodeTestTask.java:123) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.prepare(NodeTestTask.java:123) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:90) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) 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:1511) 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:235) at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54) Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1804) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1154) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:908) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:734) at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:132) at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:141) at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:90) ... 71 more Caused by: org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment] at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:275) at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:237) at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:214) at org.hibernate.id.factory.internal.DefaultIdentifierGeneratorFactory.injectServices(DefaultIdentifierGeneratorFactory.java:175) at org.hibernate.service.internal.AbstractServiceRegistryImpl.injectDependencies(AbstractServiceRegistryImpl.java:286) at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:243) at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:214) at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.<init>(InFlightMetadataCollectorImpl.java:173) at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:127) at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:1460) at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1494) at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:58) at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365) at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396) at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1863) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800) ... 86 more Caused by: org.hibernate.HibernateException: Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set at org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl.determineDialect(DialectFactoryImpl.java:100) at org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl.buildDialect(DialectFactoryImpl.java:54) at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:138) at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:35) at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.initiateService(StandardServiceRegistryImpl.java:101) at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:263) ... 103 more application.ymlMember.javaMemberRepository.javaMemberRepositoryTest.java
- 미해결실전! 스프링 데이터 JPA
Spring data JPA 각 메소드에 대해서 @Transactional이 있다면
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]@Transactional이 끝날 때 커밋을 해주는 것으로 알고 있습니다. 그리고 spring data jpa 메소드마다 transactional이 있는 것으로 알고 있습니다.그렇다면 service 에서 transactional로 선언되어 있다고 하더라도 service의 한 메소드 안에서 여러개의 spring data jpa 메소드가 사용될 때 service 메소드 단위가 아닌 spring data jpa 메소드마다 트랜잭션이 발생하는 문제가 생기지 않나요?
- 미해결실전! 스프링 데이터 JPA
프로젝트 진행시 현업에서는 어떤 식으로 진행되나요?
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]안녕하십니까. 강의 정말 잘 듣고 실무에서 적용하고자 하는 초보 개발자 입니다.다름이 아니라 프로젝트 진행시 궁금한 점이 있어 질문을 하게 되었습니다.실무에서는 기획서 -> UML 작성 -> ERD 작성 -> DB 구축 -> 백엔드 개발 시작 -> 완료후 프론트 엔드 개발 순으로 보면 되는 것일까요?API 설계시 화면 위주의 설계가 많을 까요? 아니면 도메인 위주의 API 가 많을까요? 물론 비즈니스에 따라 다르지만 오픈 API가 아닐 경우가 궁금합니다.
- 미해결실전! 스프링 데이터 JPA
bulkAgePlus시 자동으로 update되게는 못하나요?
bulkAgePlus시 자동으로 update되게는 못하나요?예를 들어...보통 컬럼을 수정하려면 데이터를 수정한 사람이 누구인지, 언제 수정 되었는지 같은 default 데이터가 자동으로 등록되게 하는경우가 매우 많은데요(예시 : modifyDate (Date), modifyBy(String))Spring data jpa 경우에는 어떤 방식으로 이것을 모든 entity update 시 처리하는지 잘 모르겠습니다.
- 미해결실전! 스프링 데이터 JPA
안녕하세요. jpa save가 안되는 문제가 있습니다.
이렇게 테스트를 작성하고 테스트를 돌려보면findById에서 아무것도 찾지 못합니다.디비 연결이 문젠가 싶었지만디비에 이미 있는 데이터는 조회가 잘 됩니다. save한 데이터를 찾으면 찾아지지 않습니다.로그를 봐도 insert쿼리도 안나가고, save만 한 뒤 테스트를해보아도 데이터가 들어가있지 않습니다. 어떤게 문제인지 알 수 있을까요?
- 해결됨실전! 스프링 데이터 JPA
자식 컬렉션 Order by 질문 사항있습니다
안녕하세요 JPA 수강하면서 현업에적용하고있는데 질문사항 있어 글을 남깁니다.store -< events 1:N 관계에 있어가게들중 이벤트를 최근에 생성한 가게들 우선순위로 가게 리스트를보여주고 싶지만 이벤트랑 조인할 경우 이벤트들이 여러개가 나와가게에 @OneToMany events 최근 생성된 데이터가 있을경우 가게를 위로 올리고싶은데방법이 있을까요 ?그리고 Store-< Events 에서 이벤트가 끝난 데이터들은 List 제외 시키고 싶은데mapping 할때 조건을 넣어서 따로 걸러야 하는 부분인가요 ?조건을 걸기위해 조인 하는순간 row수가 배가 되어원하는 list값만 가지고 오기가 어렵네요 위 내용과 상관없이 궁금사항 하나 더 남깁니다.위경도 좌표 거리 기반으로 가게를 보여주고 싶으나jpa에선 쉽지않아 nativeQuery(mysql 8.0) 로 작성 하여 interface로 매핑후 다시 dto로 가공 해서반환값을 주는데 다른 방법이 있는지 궁금 합니다. 추가 질문사항입니다. nativeQuery로 위경도 가까운 거리 계산 하여 리스트 생성하여 가게 인덱스에 담고 jpa findAllByIdx in :list 로 가게 관련 정보를 추출하였으나 자동 정렬되어 가까운 거리 순으로 리스트가 안만들어지는데 이럴때는 jdbc로 구현해야 하나요 ?? 페이징 처리까지 하려니 가급적이면 jpa를 쓰려고하는데 자동정렬 해제-> in 절 순서대로 넣은값으로 출력이 안되나요 ?조언 부탁드립니다. 감사합니다.
- 미해결실전! 스프링 데이터 JPA
트랜잭셔널
안녕하세요. Transactional과 관련해서 질문이 있어 드립니다. Service layer에서 @Transactional을 붙였을 때는 실행 중에 문제가 발생했을 경우 마지막 commit 시점까지 Rollback이 되고 만약 문제가 발생하지 않았을 경우에는 Rollback이 되지 않았습니다. 그런데 Test에서는 @Transactional 붙여야만 Rollback이 되면서 DB에 반영이 안된다고 하셨는데 Test 부분에서 사용하는 @Transactional과 Service쪽에서 사용하는 @Transactional의 역할이 다른가요?
- 해결됨실전! 스프링 데이터 JPA
실무에서 다대일 관계일 때 다 쪽에 접근이 부자연스러운 경우
안녕하세요. 강의를 들으며 실무에 적용 중인데 궁금한 것이 있어서 질문 드립니다.현재 메뉴 테이블과 메뉴권한 테이블 두개를 생성하였고 메뉴 : 메뉴 접근 권한= 1 : n 의 연관관계를 가지고 있습니다.(메뉴 권한 테이블이 주인이고 양방향이며, 하나의 메뉴에는 여러 접근 권한이 들어갈 수 있습니다.)이 상황에서 해당 테이블을 통해 작업 되는 기능은 아래와 같습니다.메뉴 명을 변경할 수 있다. (메뉴 테이블 update)이럴 경우 Spring Data JPA를 적용한다면, 상기 기능을 처리 할 때마다 메뉴Repository가 아닌 메뉴권한Repository를 호출하여 메뉴권한 객체를 통해 메뉴를 수정해야 하는데 이 부분이 좀 부자연스럽게 느껴집니다.이럴 경우 어떻게 처리 하는 것이 좀 더 나을까요? 제 생각에는테이블을 다시 짜서 메뉴가 다대일 관계로 구성 되겠끔 처리한다(지만 쉽지 않은 상황이네요)메뉴-메뉴권한 관계를 일대다 관계로 처리한다. (단방향)연관관계는 그대로 두고 저장/수정 시에만 JdbcTemplate 등으로 처리 한다.등이 있는데 더 나은 방안이 있을까요? 맨날 SQL로 작업 하다 ORM을 해보려고하는데 참 쉽지 않네요.
- 미해결실전! 스프링 데이터 JPA
비식별자 연관관계 및 페치 조인관련 질문 있습니다!
안녕하세요 강사님! 현재 JPA (Spring Data JPA + Querydsl)로 시스템을 구성하고 있는 중에 비식별자 연관관계 조인 및 fetch 조인 관련 되어 질문이 있어서 글을 쓰게 되었습니다. 거두절미하고 질문 드려볼게요.- 비식별자 연관관계PK가 아닌 UK로 연관관계를 맺어야 할 경우 @JoinColumn의 referencedColumnName 를 통해서 조인은 가능한 것이 확인 되었습니다. 다만 이럴 경우 fetchType을 LAZY로 해도 EAGER 처럼 한번에 조회가 되더라고요!확인해 보니 하이버네이트 내부에서 Lazy 로딩 시에 외부 엔티티의 프록시 객체를 생성함에 있어 PK를 기준으로 관리를 진행하는데 PK에 대한 정보가 없기 때문에 프록시 객체 구성을 하기 위해 fetch를 무시하고 바로 EAGER로 조회 하는 것으로 알고 있습니다.위에 제가 이해한 내용이 맞는지... 그리고 혹시 실무에서 이런 경우가 발생 할 경우 어떻게 처리 하는지 궁금합니다!제 생각에는 해당 엔티티만 따로 SQL(JdbcTemplate)로 처리 해야할 것으로 생각 되는데, 혹시 다른 방안이 있나 궁금하네요!(참고로 현재 저희는 연관관계상에 UK를 모두 배제하고 PK를 하기로 결정하였습니다. )- fetch 조인 관련실무 관련 질문입니다. JPA 경우 LAZY 로딩을 통해서 원하는 시점에서 쿼리를 해 오는 것으로 이해를 했습니다. (프록시 및 영속성 컨텍스트)다만 SQL 기반으로 개발을 할 경우 어떤 기능 구현을 위해 쿼리를 할 경우 대부분 조인이 되어 있는 완성형 쿼리만을 사용하여 한번만 쿼리를 날리는데, JPA 경우 LAZY를 적극적으로 활용한다면 쿼리 횟수가 늘어날 것이고 이를 방지하기 위해 fetch 조인문이 많아질 거라고 생각이 듭니다.실제 JPA를 실무에서 진행할 경우 기능 구현 시 단일 테이블 조회 기능 외 대부분 fetch 조인 쿼리를 사용하는지 그 빈도가 궁금합니다.혹시 질문 내용이 너무 추상적이면 말씀해주세요! 다시 정리 하여 질문 드리겠습니다. 감사합니다!
- 미해결실전! 스프링 데이터 JPA
fk 설정 문제 재문의
답변 감사 했습니다.제가 의도 했던 질문은 연관관계를 매핑 하는데 있어서테이블에 fk 정의를 하지 않았을 때 문제가 있는지 여부 였습니다.아래와 같이 연관 관계 정의는 하였으나,@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "team_id")Member 테이블에서 team_id 에 대한 fk 설정(CONSTRAINT member_fk1 FOREIGN KEY (`team_id`)) REFERENCE ~ )하지 않아도 무방한 건지 아님 문제가 되는지 궁금 합니다 ~
- 미해결실전! 스프링 데이터 JPA
연관관계 설정 시 fk 필수여부
안녕하세요 ~@JoinColumn 으로 fk 설정 하는데요.테이블에 반드시 fk 설정을 한 후 사용해야 하는 건가요?
- 미해결실전! 스프링 데이터 JPA
Spring Data Jpa findByUsername 질문
안녕하세요.memberRepository.findByUsername("member5");했을시, select query가 나가는데, 영속성컨텍스트에서 가져오는게 아니라 db에서 가져오는 것이 아닌가요? select 쿼리가 나가는데 왜 age가 40인지 잘 모르겠습니다.
- 해결됨실전! 스프링 데이터 JPA
페이징 했을때 발생하는 문제
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]안녕하세여 영한님 강의 너무 잘 보고있습니다.다름이 아니라 JPA 관련 강의를 모두 듣고 사이드 프로젝트에JPA를 적용하던 중에 문제가 발생하여 질문드립니다.문제 상황현재 Issue 라는 엔티티가 member와 milestone이라는 엔티티와 @ManyToOne 관계를 맺고 있는 와중에 Issue의 상태에 따라 Issue를 조회 하는 로직을 만들고 있었습니다.문제 코드@Query(value = "select i from Issue i left join fetch i.member m left join fetch i.milestone mi where i.status = :status") Page<Issue> findIssues(Pageable pageable, @Param("status") IssueStatus status);위에 코드가 상태에 따라 Issue를 조회 하는 코드인데, 우선 N+1 문제를 해결하기 위해 left join fetch 를 사용하고 Issue의 상태에 따른 조회를 위해 where 문을 사용하였습니다. 작성한 코드가 크게 문제가 있다 생각하지 않았는데 막상 실행을 해보니 query specified join fetching, but the owner of the fetched association was not present in the select list 라는 문구의 예외가 발생하였습니다.다행히 구글에 검색하여https://stackoverflow.com/questions/12459779/query-specified-join-fetching-but-the-owner-of-the-fetched-association-was-not해당 글을 보고 따로 count query를 작성하여 해결을 하긴 했는데, 왜 여기서 따로 count query를 작성해야 하는지 의문이 들었습니다. 페이징을 하면 count query를 알아서 날려주는 것으로 생각했는데 여기서는 왜 그렇게 동작하지 않는걸까요??해결 코드@Query(value = "select i from Issue i left join fetch i.member m left join fetch i.milestone mi where i.status = :status", countQuery = "select count(i) from Issue i left join i.member left join i.milestone where i.status = :status") Page<Issue> findIssues(Pageable pageable, @Param("status") IssueStatus status);