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

[feat/CK-236] JdbcTemplate을 이용하여 bulk insert를 적용한다 #198

Merged
merged 2 commits into from
Dec 2, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이미 Service가 Repository 계층에 의존하고 있으니 DAO 대신 Repository 계층을 만드는건 어떨까요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이미 JpaRepository를 상속받는 GoalRoomMemberRepository가 존재해서 만들기가 애매했어요..!

그래서 기존 GoalRoomMemberRepositoryGoalRoomMemberJdbcRepository 라는 인터페이스를 상속받도록 했어요! GoalRoomMemberJdbcRepository 인터페이스는 saveAllInBatch라는 메서드를 구현하도록 했고, GoalRoomMemberJdbcRepositoryImpl 구현체를 만들어서 batch insert 작업해주었습니다.
구조가 조금 복잡한 것 같은데 괜찮은지 확인부탁드립니다!

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package co.kirikiri.persistence.goalroom;

import co.kirikiri.domain.goalroom.GoalRoomMember;
import java.util.List;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class GoalRoomMemberDao {

private final JdbcTemplate jdbcTemplate;

public GoalRoomMemberDao(final JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}

public void saveAll(final List<GoalRoomMember> goalRoomMembers) {
final String sql = "INSERT INTO goal_room_member "
+ "(goal_room_id, member_id, role, joined_at, accomplishment_rate)"
+ "VALUES (?, ?, ?, ?, ?)";
jdbcTemplate.batchUpdate(sql, goalRoomMembers, goalRoomMembers.size(), ((ps, goalRoomMember) -> {
ps.setLong(1, goalRoomMember.getGoalRoom().getId());
ps.setLong(2, goalRoomMember.getMember().getId());
ps.setString(3, goalRoomMember.getRole().name());
ps.setObject(4, goalRoomMember.getJoinedAt());
ps.setDouble(5, goalRoomMember.getAccomplishmentRate());
}));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
import co.kirikiri.domain.goalroom.GoalRoom;
import co.kirikiri.domain.goalroom.GoalRoomPendingMember;
import co.kirikiri.domain.member.vo.Identifier;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
import java.util.Optional;

public interface GoalRoomPendingMemberRepository extends JpaRepository<GoalRoomPendingMember, Long>,
GoalRoomPendingMemberQueryRepository {
Expand All @@ -28,4 +29,8 @@ Optional<GoalRoomPendingMember> findByGoalRoomAndMemberIdentifier(
+ "where g=:goalRoom "
+ "and gp.member = m")
List<GoalRoomPendingMember> findAllByGoalRoom(@Param("goalRoom") final GoalRoom goalRoom);

@Modifying
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Modifying을 붙여야 하는 이유가 JPA에서 동작을 추론가능한 메서드명일 경우라서 그런건가요?

@Query("DELETE FROM GoalRoomPendingMember gp WHERE gp.id IN :ids")
void deleteAllByIdIn(@Param("ids") final List<Long> ids);
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ public List<GoalRoom> findByRoadmap(final Roadmap roadmap) {
@Override
public List<GoalRoom> findAllRecruitingGoalRoomsByStartDateEarlierThan(final LocalDate date) {
return selectFrom(goalRoom)
.innerJoin(goalRoom.goalRoomPendingMembers.values, goalRoomPendingMember)
.fetchJoin()
Comment on lines +119 to +120
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

join이 안되어있었네요..👍

.where(statusCond(GoalRoomStatus.RECRUITING))
.where(equalOrEarlierStartDateThan(date))
.fetch();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package co.kirikiri.persistence.goalroom;

import co.kirikiri.domain.goalroom.GoalRoom;
import org.springframework.data.jpa.repository.JpaRepository;
import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface GoalRoomRepository extends JpaRepository<GoalRoom, Long>, GoalRoomQueryRepository {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package co.kirikiri.service.scheduler;

import co.kirikiri.domain.BaseEntity;
import co.kirikiri.domain.goalroom.GoalRoom;
import co.kirikiri.domain.goalroom.GoalRoomMember;
import co.kirikiri.domain.goalroom.GoalRoomPendingMember;
import co.kirikiri.persistence.goalroom.GoalRoomMemberDao;
import co.kirikiri.persistence.goalroom.GoalRoomPendingMemberRepository;
import co.kirikiri.persistence.goalroom.GoalRoomRepository;
import co.kirikiri.service.aop.ExceptionConvert;
import java.time.LocalDate;
Expand All @@ -19,23 +22,25 @@
public class GoalRoomScheduler {

private final GoalRoomRepository goalRoomRepository;
private final GoalRoomPendingMemberRepository goalRoomPendingMemberRepository;
private final GoalRoomMemberDao goalRoomMemberDao;

@Scheduled(cron = "0 0 0 * * *")
public void startGoalRooms() {
final List<GoalRoom> goalRoomsToStart = goalRoomRepository.findAllRecruitingGoalRoomsByStartDateEarlierThan(
LocalDate.now());
for (final GoalRoom goalRoom : goalRoomsToStart) {
final List<GoalRoomPendingMember> goalRoomPendingMembers = goalRoom.getGoalRoomPendingMembers().getValues();
saveGoalRoomMemberFromPendingMembers(goalRoomPendingMembers, goalRoom);
saveGoalRoomMemberFromPendingMembers(goalRoomPendingMembers);
goalRoom.start();
}
}

private void saveGoalRoomMemberFromPendingMembers(final List<GoalRoomPendingMember> goalRoomPendingMembers,
final GoalRoom goalRoom) {
private void saveGoalRoomMemberFromPendingMembers(final List<GoalRoomPendingMember> goalRoomPendingMembers) {
final List<GoalRoomMember> goalRoomMembers = makeGoalRoomMembers(goalRoomPendingMembers);
goalRoom.addAllGoalRoomMembers(goalRoomMembers);
goalRoom.deleteAllPendingMembers();
goalRoomMemberDao.saveAll(goalRoomMembers);
final List<Long> ids = makeGoalRoomPendingMemberIds(goalRoomPendingMembers);
goalRoomPendingMemberRepository.deleteAllByIdIn(ids);
}

private List<GoalRoomMember> makeGoalRoomMembers(final List<GoalRoomPendingMember> goalRoomPendingMembers) {
Expand All @@ -45,9 +50,14 @@ private List<GoalRoomMember> makeGoalRoomMembers(final List<GoalRoomPendingMembe
}

private GoalRoomMember makeGoalRoomMember(final GoalRoomPendingMember goalRoomPendingMember) {
return new GoalRoomMember(goalRoomPendingMember.getRole(),
goalRoomPendingMember.getJoinedAt(), goalRoomPendingMember.getGoalRoom(),
goalRoomPendingMember.getMember());
return new GoalRoomMember(goalRoomPendingMember.getRole(), goalRoomPendingMember.getJoinedAt(),
goalRoomPendingMember.getGoalRoom(), goalRoomPendingMember.getMember());
}

private List<Long> makeGoalRoomPendingMemberIds(final List<GoalRoomPendingMember> goalRoomPendingMembers) {
return goalRoomPendingMembers.stream()
.map(BaseEntity::getId)
.toList();
}

@Scheduled(cron = "0 0 4 * * *")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@
import static co.kirikiri.domain.goalroom.GoalRoomStatus.RUNNING;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

Expand Down Expand Up @@ -35,19 +34,19 @@
import co.kirikiri.domain.roadmap.RoadmapNodeImage;
import co.kirikiri.domain.roadmap.RoadmapNodeImages;
import co.kirikiri.domain.roadmap.RoadmapNodes;
import co.kirikiri.persistence.goalroom.GoalRoomMemberRepository;
import co.kirikiri.persistence.goalroom.GoalRoomMemberDao;
import co.kirikiri.persistence.goalroom.GoalRoomPendingMemberRepository;
import co.kirikiri.persistence.goalroom.GoalRoomRepository;
import co.kirikiri.service.scheduler.GoalRoomScheduler;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;

@ExtendWith(MockitoExtension.class)
class GoalRoomSchedulerTest {
Expand All @@ -60,10 +59,10 @@ class GoalRoomSchedulerTest {
private GoalRoomRepository goalRoomRepository;

@Mock
private GoalRoomMemberRepository goalRoomMemberRepository;
private GoalRoomPendingMemberRepository goalRoomPendingMemberRepository;

@Mock
private GoalRoomPendingMemberRepository goalRoomPendingMemberRepository;
private GoalRoomMemberDao goalRoomMemberDao;

@InjectMocks
private GoalRoomScheduler goalRoomScheduler;
Expand All @@ -83,9 +82,9 @@ class GoalRoomSchedulerTest {
final Member follower2 = 사용자를_생성한다(3L, "identifier2", "password3!", "name2", "kirikiri@email.com");
final Member follower3 = 사용자를_생성한다(4L, "identifier3", "password4!", "name3", "kirikiri@email.com");

final GoalRoomPendingMember goalRoomPendingMember = 골룸_대기자를_생성한다(goalRoom2, creator, GoalRoomRole.FOLLOWER);
final GoalRoomPendingMember goalRoomPendingMember1 = 골룸_대기자를_생성한다(goalRoom1, follower1, GoalRoomRole.FOLLOWER);
final GoalRoomPendingMember goalRoomPendingMember2 = 골룸_대기자를_생성한다(goalRoom1, follower2, GoalRoomRole.FOLLOWER);
골룸_대기자를_생성한다(goalRoom2, creator, GoalRoomRole.FOLLOWER);
골룸_대기자를_생성한다(goalRoom1, follower1, GoalRoomRole.FOLLOWER);
골룸_대기자를_생성한다(goalRoom1, follower2, GoalRoomRole.FOLLOWER);

goalRoom1.join(follower1);
goalRoom1.join(follower2);
Expand Down Expand Up @@ -130,9 +129,7 @@ class GoalRoomSchedulerTest {
goalRoomScheduler.startGoalRooms();

// then
verify(goalRoomPendingMemberRepository, times(0)).findAllByGoalRoom(any());
verify(goalRoomMemberRepository, times(0)).saveAll(anyList());
verify(goalRoomPendingMemberRepository, times(0)).deleteAll(anyList());
verify(goalRoomPendingMemberRepository, never()).deleteAllByIdIn(anyList());

assertAll(
() -> assertThat(goalRoom1.getStatus()).isEqualTo(RECRUITING),
Expand Down