작성
·
318
·
수정됨
0
안녕하세요. 강의 잘 듣고 있습니다. 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);
}
답변 2
0
0
test 통과를 못한 이유는 test code에 있는 catch(Exception)에서 계속 postOrder에 있는 memberService.isPresentMember(requestDto.getMemberId())을 통과하지 못합니다. isPresentMember은 repository에 있는 findByMemberId(Long id)이고요!(실제 코드는 orderService.postOrderWithPessimisticWrite가 아닌 orderService.postOrder로 정상적으로 되어 있습니다.)