Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Choice에서 ChoiceContent를 지연 로딩한다 #188

Merged
merged 10 commits into from
Mar 8, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Optional;

import static life.offonoff.ab.application.service.common.LengthInfo.COMMENT_CONTENT;

@RequiredArgsConstructor
Expand Down Expand Up @@ -127,8 +125,8 @@ private void validateContent(String content) {

//== like ==//
@Transactional
public CommentReactionResponse likeCommentForMember(final Long memberId, final Long commentId, final Boolean enable) {
Member liker = findMember(memberId);
public CommentReactionResponse likeCommentByMember(final Long likerId, final Long commentId, final Boolean enable) {
Member liker = findMember(likerId);
Comment comment = findById(commentId);

if (enable) {
Expand All @@ -138,21 +136,15 @@ public CommentReactionResponse likeCommentForMember(final Long memberId, final L
}

private CommentReactionResponse doLike(Member liker, Comment comment) {

liker.cancelHateIfExists(comment);
Optional<LikedComment> optionalLikedComment = liker.likeCommentIfNew(comment);

publishCommentLikedEventIfPresent(optionalLikedComment);

return new CommentReactionResponse(comment.getLikeCount(), comment.getHateCount(), true, false);
liker.likeCommentIfNew(comment)
.ifPresent(this::afterLike);
return new CommentReactionResponse(
comment.getLikeCount(), comment.getHateCount(), true, false);
}

private void publishCommentLikedEventIfPresent(Optional<LikedComment> optional) {
if (optional.isPresent()) {
LikedComment likedComment = optional.get();

eventPublisher.publishEvent(new CommentLikedEvent(likedComment));
}
private void afterLike(LikedComment likedComment) {
eventPublisher.publishEvent(new CommentLikedEvent(likedComment));
}

private CommentReactionResponse cancelLike(Member liker, Comment comment) {
Expand Down
7 changes: 2 additions & 5 deletions src/main/java/life/offonoff/ab/domain/keyword/Keyword.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
import life.offonoff.ab.domain.topic.Topic;
import life.offonoff.ab.domain.topic.TopicSide;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.List;

@Getter
@EqualsAndHashCode(of = {"name", "side"})
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Table(uniqueConstraints = {
Expand All @@ -37,9 +39,4 @@ public Keyword(String name, TopicSide side) {
public void addTopic(Topic topic) {
this.topics.add(topic);
}

@Override
public boolean equals(Object obj) {
return id.equals(((Keyword) obj).getId());
}
}
2 changes: 1 addition & 1 deletion src/main/java/life/offonoff/ab/domain/topic/Topic.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class Topic extends BaseEntity {
@JoinColumn(name = "topic_content_id")
private TopicContent content;

@OneToMany(mappedBy = "topic", cascade = CascadeType.REMOVE)
@OneToMany(mappedBy = "topic", cascade = CascadeType.ALL)
private List<Choice> choices = new ArrayList<>();

@Enumerated(EnumType.STRING)
Expand Down
10 changes: 9 additions & 1 deletion src/main/java/life/offonoff/ab/domain/topic/choice/Choice.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import life.offonoff.ab.domain.BaseEntity;
import life.offonoff.ab.domain.topic.Topic;
import life.offonoff.ab.domain.topic.choice.content.ChoiceContent;
import life.offonoff.ab.web.response.topic.choice.content.ChoiceContentResponse;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
Expand All @@ -23,7 +24,7 @@ public class Choice extends BaseEntity {
@Enumerated(EnumType.STRING)
private ChoiceOption choiceOption;

@OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "choice_content_id")
private ChoiceContent content;

Expand All @@ -50,4 +51,11 @@ public void increaseVoteCount() {
public void decreaseVoteCount() {
this.voteCount--;
}

public ChoiceContentResponse generateContentResponse() {
if (content == null) {
return null;
}
return content.toResponse();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import jakarta.persistence.*;
import life.offonoff.ab.domain.BaseEntity;
import life.offonoff.ab.web.response.topic.choice.content.ChoiceContentResponse;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
Expand All @@ -15,4 +16,6 @@ public abstract class ChoiceContent extends BaseEntity {

@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

public abstract ChoiceContentResponse toResponse();
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package life.offonoff.ab.domain.topic.choice.content;

import jakarta.persistence.*;
import jakarta.persistence.Column;
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Entity;
import life.offonoff.ab.web.response.topic.choice.content.ChoiceContentResponse;
import life.offonoff.ab.web.response.topic.choice.content.ChoiceContentResponse.ImageTextChoiceContentResponse;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
Expand All @@ -18,4 +22,9 @@ public ImageTextChoiceContent(String imageUrl, String text) {
this.imageUrl = imageUrl;
this.text = text;
}

@Override
public ChoiceContentResponse toResponse() {
return new ImageTextChoiceContentResponse(imageUrl, text);
}
}
2 changes: 1 addition & 1 deletion src/main/java/life/offonoff/ab/web/CommentController.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public ResponseEntity<CommentReactionResponse> likeComment(
@PathVariable Long commentId,
@RequestParam Boolean enable
) {
return ResponseEntity.ok(commentService.likeCommentForMember(memberId, commentId, enable));
return ResponseEntity.ok(commentService.likeCommentByMember(memberId, commentId, enable));
}

@PostMapping("/{commentId}/hate")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@

import life.offonoff.ab.domain.topic.choice.Choice;
import life.offonoff.ab.domain.topic.choice.ChoiceOption;
import life.offonoff.ab.web.response.topic.choice.content.ChoiceContentResponseFactory;
import life.offonoff.ab.web.response.topic.choice.content.ChoiceContentResponseFactory.ChoiceContentResponse;
import life.offonoff.ab.web.response.topic.choice.content.ChoiceContentResponse;
import lombok.Getter;

@Getter
public class ChoiceResponse {

private Long choiceId;
private ChoiceContentResponse content;
private ChoiceOption choiceOption;
private final Long choiceId;
private final ChoiceContentResponse content;
private final ChoiceOption choiceOption;

public ChoiceResponse(Long choiceId, ChoiceContentResponse content, ChoiceOption choiceOption) {
this.choiceId = choiceId;
Expand All @@ -22,7 +21,7 @@ public ChoiceResponse(Long choiceId, ChoiceContentResponse content, ChoiceOption
public static ChoiceResponse from(Choice choice) {
return new ChoiceResponse(
choice.getId(),
ChoiceContentResponseFactory.create(choice.getContent()),
choice.generateContentResponse(),
choice.getChoiceOption());
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
package life.offonoff.ab.web.response.topic.choice;

import life.offonoff.ab.domain.topic.choice.Choice;
import life.offonoff.ab.web.response.topic.choice.content.ChoiceContentResponseFactory;
import lombok.Getter;

@Getter
public class ChoiceResponseWithCount extends ChoiceResponse {

private int voteCount;
private final int voteCount;

public ChoiceResponseWithCount(Choice choice) {
super(choice.getId(),
ChoiceContentResponseFactory.create(choice.getContent()),
choice.generateContentResponse(),
choice.getChoiceOption());

this.voteCount = choice.getVoteCount();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package life.offonoff.ab.web.response.topic.choice.content;

import life.offonoff.ab.domain.topic.choice.content.ChoiceContentType;

public interface ChoiceContentResponse {
record ImageTextChoiceContentResponse(
String imageUrl,
String text,
String type
) implements ChoiceContentResponse {
public ImageTextChoiceContentResponse(String imageUrl, String text) {
this(imageUrl, text, ChoiceContentType.IMAGE_WITH_TEXT_CHOICE_CONTENT);
}
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ void like_with_cancel_hate() {
liker.hateCommentIfNew(comment);

// when
commentService.likeCommentForMember(liker.getId(), comment.getId(), true);
commentService.likeCommentByMember(liker.getId(), comment.getId(), true);

// then
assertAll(
Expand Down Expand Up @@ -382,7 +382,7 @@ private Long createRandomMember() {
}

private Long createRandomTopic() {
Topic topic = createTopic(0, TopicSide.TOPIC_B);
Topic topic = createRandomTopicByRandomMember(TopicSide.TOPIC_B);
em.persist(topic);
return topic.getId();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@
import life.offonoff.ab.exception.DuplicateVoteOptionException;
import life.offonoff.ab.exception.LengthInvalidException;
import life.offonoff.ab.repository.ChoiceRepository;
import life.offonoff.ab.repository.keyword.KeywordRepository;
import life.offonoff.ab.repository.VoteRepository;
import life.offonoff.ab.repository.comment.CommentRepository;
import life.offonoff.ab.repository.keyword.KeywordRepository;
import life.offonoff.ab.repository.member.MemberRepository;
import life.offonoff.ab.repository.topic.TopicRepository;
import life.offonoff.ab.web.response.topic.choice.ChoiceResponse;
import life.offonoff.ab.web.response.topic.choice.content.ImageTextChoiceContentResponse;
import life.offonoff.ab.web.response.KeywordResponse;
import life.offonoff.ab.web.response.topic.TopicResponse;
import life.offonoff.ab.web.response.topic.choice.ChoiceResponse;
import life.offonoff.ab.web.response.topic.choice.content.ChoiceContentResponse.ImageTextChoiceContentResponse;
import lombok.Builder;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
Expand All @@ -42,8 +42,6 @@
import java.util.Optional;

import static life.offonoff.ab.domain.TestEntityUtil.*;
import static life.offonoff.ab.domain.TestEntityUtil.TestKeyword;
import static life.offonoff.ab.domain.TestEntityUtil.TestMember;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertAll;
Expand Down Expand Up @@ -457,8 +455,8 @@ public TopicResponse createResponse() {
choiceResponses.add(new ChoiceResponse(
(long) i,
new ImageTextChoiceContentResponse(
((ImageTextChoiceContentCreateRequest) choice.choiceContentRequest()).text(),
((ImageTextChoiceContentCreateRequest) choice.choiceContentRequest()).imageUrl()),
((ImageTextChoiceContentCreateRequest) choice.choiceContentRequest()).imageUrl(),
((ImageTextChoiceContentCreateRequest) choice.choiceContentRequest()).text()),
choice.choiceOption()));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.transaction.annotation.Transactional;

import static life.offonoff.ab.domain.TestEntityUtil.*;
import static org.mockito.Mockito.*;

@Transactional
@SpringBootTest
class TopicEventHandlerTest {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,23 @@
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static life.offonoff.ab.domain.TestEntityUtil.*;
import static life.offonoff.ab.domain.topic.TopicStatus.*;
import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;
import static life.offonoff.ab.domain.TestEntityUtil.TestMember;
import static life.offonoff.ab.domain.TestEntityUtil.TestTopic;
import static life.offonoff.ab.domain.topic.TopicStatus.CLOSED;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.mockito.Mockito.*;

@Transactional
@SpringBootTest
@Import(TestVoteConfig.TestContainerVotingTopicConfig.class)
public class VotingTopicContainerServiceIntegrationTest {
Expand All @@ -45,7 +51,7 @@ public class VotingTopicContainerServiceIntegrationTest {

@Test
@DisplayName("투표가 끝난 토픽은 status 수정 & Voting Result 매핑")
void endVote_then_status_voting_result() {
void endVote_then_status_voting_result() throws InterruptedException {
// given
LocalDateTime deadline = LocalDateTime.now();
Topic topic = TestTopic.builder()
Expand All @@ -59,7 +65,19 @@ void endVote_then_status_voting_result() {
when(container.getVotingEnded(criteria)).thenReturn(votingTopics);

// when
votingTopicContainerService.endVote(criteria);
ExecutorService executorService = Executors.newFixedThreadPool(2);
CountDownLatch latch = new CountDownLatch(2);

executorService.execute(() -> {
votingTopicContainerService.endVote(criteria);
latch.countDown();
});

executorService.execute(() -> {
votingTopicContainerService.endVote(criteria);
latch.countDown();
});
latch.await();

// then
assertThat(topic.getStatus()).isEqualTo(CLOSED);
Expand Down
Loading
Loading