Skip to content

Commit 631a125

Browse files
authored
Merge pull request #266 from Team-Capple/feat/#265/entityConcurrentIssue
[FEAT] ์ข‹์•„์š”, ๋Œ“๊ธ€ ๊ฐœ์ˆ˜ ๋™์‹œ์„ฑ ๋ฌธ์ œ ํ•ด๊ฒฐ
2 parents 1109c7a + fd751fc commit 631a125

39 files changed

+2144
-102
lines changed

โ€Žbuild.gradleโ€Ž

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ dependencies {
7070
implementation 'io.micrometer:micrometer-registry-prometheus'
7171
// open csv
7272
implementation 'com.opencsv:opencsv:5.5.1'
73+
// redisson
74+
implementation 'org.redisson:redisson-spring-boot-starter:3.46.0'
7375
}
7476

7577
dependencyManagement {

โ€Žconfigโ€Ž

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.server.capple.config;
2+
3+
import org.redisson.Redisson;
4+
import org.redisson.api.RedissonClient;
5+
import org.redisson.config.Config;
6+
import org.springframework.beans.factory.annotation.Value;
7+
import org.springframework.context.annotation.Bean;
8+
import org.springframework.context.annotation.Configuration;
9+
10+
@Configuration
11+
public class RedissonConfig {
12+
@Value("${spring.data.redis.host}")
13+
private String redisHost;
14+
@Value("${spring.data.redis.port}")
15+
private String redisPort;
16+
@Value("${spring.data.redis.database}")
17+
private Integer redisDatabase;
18+
@Bean
19+
public RedissonClient redissonClient() {
20+
Config config = new Config();
21+
config.useSingleServer().setAddress("redis://" + redisHost + ":" + redisPort).setDatabase(redisDatabase);
22+
return Redisson.create(config);
23+
}
24+
}

โ€Žsrc/main/java/com/server/capple/domain/answer/entity/Answer.javaโ€Ž

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import lombok.*;
99
import org.hibernate.annotations.ColumnDefault;
1010
import org.hibernate.annotations.DynamicInsert;
11+
import org.hibernate.annotations.DynamicUpdate;
1112
import org.hibernate.annotations.SQLRestriction;
1213

1314
@Getter
@@ -17,6 +18,7 @@
1718
@AllArgsConstructor
1819
@SQLRestriction("deleted_at is null")
1920
@DynamicInsert
21+
@DynamicUpdate
2022
public class Answer extends BaseEntity {
2123
@Id
2224
@GeneratedValue(strategy = GenerationType.IDENTITY)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.server.capple.domain.answer.service;
2+
3+
import com.server.capple.domain.answer.entity.Answer;
4+
import com.server.capple.domain.answer.repository.AnswerRepository;
5+
import com.server.capple.global.utils.distributedLock.DistributedLock;
6+
import lombok.RequiredArgsConstructor;
7+
import org.springframework.stereotype.Service;
8+
9+
@Service
10+
@RequiredArgsConstructor
11+
public class AnswerConcurrentService {
12+
private final AnswerRepository answerRepository;
13+
14+
@DistributedLock("'answer::heartCount::' + #answer.id")
15+
public Boolean setHeartCount(Answer answer, boolean isLiked) {
16+
answer = answerRepository.findById(answer.getId()).get();
17+
answer.setHeartCount(isLiked);
18+
return true;
19+
}
20+
21+
@DistributedLock("'answer::commentCount::' + #answer.id")
22+
public Boolean increaseCommentCount(Answer answer) {
23+
answer = answerRepository.findById(answer.getId()).get();
24+
answer.increaseCommentCount();
25+
return true;
26+
}
27+
28+
@DistributedLock("'answer::commentCount::' + #answer.id")
29+
public Boolean decreaseCommentCount(Answer answer) {
30+
answer = answerRepository.findById(answer.getId()).get();
31+
answer.decreaseCommentCount();
32+
return true;
33+
}
34+
}

โ€Žsrc/main/java/com/server/capple/domain/answer/service/AnswerServiceImpl.javaโ€Ž

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import com.server.capple.domain.answer.repository.AnswerHeartRedisRepository;
1515
import com.server.capple.domain.answer.repository.AnswerHeartRepository;
1616
import com.server.capple.domain.answer.repository.AnswerRepository;
17-
import com.server.capple.domain.answerComment.repository.AnswerCommentRepository;
1817
import com.server.capple.domain.member.entity.Member;
1918
import com.server.capple.domain.member.service.MemberService;
2019
import com.server.capple.domain.notifiaction.service.NotificationService;
@@ -54,6 +53,7 @@ public class AnswerServiceImpl implements AnswerService {
5453
private final NotificationService notificationService;
5554
private final AnswerHeartRepository answerHeartRepository;
5655
private final AnswerHeartMapper answerHeartMapper;
56+
private final AnswerConcurrentService answerConcurrentService;
5757

5858
@Transactional
5959
@Override
@@ -121,7 +121,9 @@ public AnswerLike toggleAnswerHeart(Member loginMember, Long answerId) {
121121
});
122122

123123
boolean isLiked = answerHeart.toggleHeart();
124-
answer.setHeartCount(answerHeart.isLiked()); // ๋‹ต๋ณ€์— ๋Œ€ํ•œ ์ข‹์•„์š” heartCount ์ฆ๊ฐ€/๊ฐ์†Œ
124+
if (!answerConcurrentService.setHeartCount(answer, isLiked)) { // ๋‹ต๋ณ€์— ๋Œ€ํ•œ ์ข‹์•„์š” heartCount ์ฆ๊ฐ€/๊ฐ์†Œ
125+
throw new RestApiException(AnswerErrorCode.ANSWER_COUNT_CHANGE_FAILED);
126+
}
125127

126128
if(isLiked)
127129
notificationService.sendAnswerHeartNotification(loginMember.getId(), answer);

โ€Žsrc/main/java/com/server/capple/domain/answerComment/entity/AnswerComment.javaโ€Ž

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import lombok.*;
88
import org.hibernate.annotations.ColumnDefault;
99
import org.hibernate.annotations.DynamicInsert;
10+
import org.hibernate.annotations.DynamicUpdate;
1011
import org.hibernate.annotations.SQLRestriction;
1112

1213
@Getter
@@ -16,6 +17,7 @@
1617
@AllArgsConstructor
1718
@SQLRestriction("deleted_at is null")
1819
@DynamicInsert
20+
@DynamicUpdate
1921
public class AnswerComment extends BaseEntity {
2022

2123
@Id
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.server.capple.domain.answerComment.service;
2+
3+
import com.server.capple.domain.answerComment.entity.AnswerComment;
4+
import com.server.capple.domain.answerComment.repository.AnswerCommentRepository;
5+
import com.server.capple.global.utils.distributedLock.DistributedLock;
6+
import lombok.RequiredArgsConstructor;
7+
import org.springframework.stereotype.Service;
8+
9+
@Service
10+
@RequiredArgsConstructor
11+
public class AnswerCommentConcurrentService {
12+
private final AnswerCommentRepository answerCommentRepository;
13+
14+
@DistributedLock("'answerComment::heartCount::' + #answerComment.id")
15+
Boolean setHeartCount(AnswerComment answerComment, boolean isLiked) {
16+
answerComment = answerCommentRepository.findById(answerComment.getId()).get();
17+
answerComment.setHeartCount(isLiked);
18+
return true;
19+
}
20+
}

โ€Žsrc/main/java/com/server/capple/domain/answerComment/service/AnswerCommentServiceImpl.javaโ€Ž

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.server.capple.domain.answerComment.service;
22

33
import com.server.capple.domain.answer.entity.Answer;
4+
import com.server.capple.domain.answer.service.AnswerConcurrentService;
45
import com.server.capple.domain.answer.service.AnswerService;
56
import com.server.capple.domain.answerComment.dto.AnswerCommentRequest;
67
import com.server.capple.domain.answerComment.dto.AnswerCommentResponse.*;
@@ -32,6 +33,8 @@ public class AnswerCommentServiceImpl implements AnswerCommentService{
3233
private final AnswerService answerService;
3334
private final NotificationService notificationService;
3435
private final AnswerSubscribeMemberService answerSubscribeMemberService;
36+
private final AnswerCommentConcurrentService answerCommentConcurrentService;
37+
private final AnswerConcurrentService answerConcurrentService;
3538

3639
/* ๋Œ“๊ธ€ ์ž‘์„ฑ */
3740
@Override
@@ -41,7 +44,9 @@ public AnswerCommentId createAnswerComment(Member member, Long answerId, AnswerC
4144
Answer answer = answerService.findAnswer(answerId);
4245
AnswerComment answerComment = answerCommentRepository.save(answerCommentMapper.toAnswerCommentEntity(loginMember, answer, request.getAnswerComment()));
4346
notificationService.sendAnswerCommentNotification(answer, answerComment);
44-
answer.increaseCommentCount(); // ๋‹ต๋ณ€ commentCount ์ฆ๊ฐ€
47+
if (!answerConcurrentService.increaseCommentCount(answer)) { // ๋‹ต๋ณ€ commentCount ์ฆ๊ฐ€
48+
throw new RestApiException(CommentErrorCode.COMMENT_COUNT_INCREASE_FAILED);
49+
}
4550
return new AnswerCommentId(answerComment.getId());
4651
}
4752

@@ -53,7 +58,9 @@ public AnswerCommentId deleteAnswerComment(Member member, Long commentId) {
5358
checkPermission(member, answerComment); // ์œ ์ € ๊ถŒํ•œ ์ฒดํฌ
5459

5560
answerSubscribeMemberService.deleteAnswerSubscribeMemberByAnswerId(answerComment.getAnswer().getId());
56-
answerComment.getAnswer().decreaseCommentCount(); // ๋‹ต๋ณ€ commentCount ๊ฐ์†Œ
61+
if (!answerConcurrentService.decreaseCommentCount(answerComment.getAnswer())) { // ๋‹ต๋ณ€ commentCount ๊ฐ์†Œ
62+
throw new RestApiException(CommentErrorCode.COMMENT_COUNT_DECREASE_FAILED);
63+
}
5764
answerComment.delete();
5865
return new AnswerCommentId(answerComment.getId());
5966
}
@@ -77,7 +84,9 @@ public AnswerCommentHeart heartAnswerComment(Member member, Long commentId) {
7784
Boolean isLiked = answerCommentHeartRedisRepository.toggleAnswerCommentHeart(commentId, member.getId());
7885
if(isLiked)
7986
notificationService.sendAnswerCommentHeartNotification(answerComment);
80-
answerComment.setHeartCount(isLiked); // ๋Œ“๊ธ€ ์ข‹์•„์š” heartCount ๊ฐ์†Œ
87+
if (!answerCommentConcurrentService.setHeartCount(answerComment, isLiked)) { // ๋Œ“๊ธ€ ์ข‹์•„์š” heartCount ๊ฐ์†Œ
88+
throw new RestApiException(CommentErrorCode.COMMENT_HEART_CHANGE_FAILED);
89+
}
8190
return new AnswerCommentHeart(commentId, isLiked);
8291
}
8392

โ€Žsrc/main/java/com/server/capple/domain/board/entity/Board.javaโ€Ž

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
package com.server.capple.domain.board.entity;
22

3-
import com.server.capple.domain.answer.dto.AnswerRequest;
4-
import com.server.capple.domain.board.dto.BoardRequest;
53
import com.server.capple.domain.member.entity.Member;
64
import com.server.capple.global.common.BaseEntity;
75
import jakarta.persistence.*;
86
import lombok.*;
97
import org.hibernate.annotations.ColumnDefault;
108
import org.hibernate.annotations.DynamicInsert;
9+
import org.hibernate.annotations.DynamicUpdate;
1110
import org.hibernate.annotations.SQLRestriction;
1211

1312
@Getter
@@ -17,6 +16,7 @@
1716
@AllArgsConstructor
1817
@SQLRestriction("deleted_at is null")
1918
@DynamicInsert
19+
@DynamicUpdate
2020
public class Board extends BaseEntity {
2121
@Id
2222
@GeneratedValue(strategy = GenerationType.IDENTITY)

0 commit comments

Comments
ย (0)