상황

전제

userVolunteerWork 엔티티

@Getter
@Entity
@Table(name = "USER_VOLUNTEERWORK", uniqueConstraints = @UniqueConstraint(columnNames = {"VOLUNTEER_WORK_ID", "USER_ID"}), indexes = @Index(name = "idx_status", columnList = "status"))
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class UserVolunteerWork extends BaseTimeEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "USER_ID")
    private User user;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "VOLUNTEER_WORK_ID")
    private VolunteerWork volunteerWork;

    @Column(name = "STATUS")
    @Enumerated(EnumType.STRING)
    private ApplyStatus status;
    
    // 낙관적 락을 위해 사용했으나 효과 X
    // @Version
    // private Long version 

    public UserVolunteerWork(User user, VolunteerWork volunteerWork) {
        this.user = user;
        this.volunteerWork = volunteerWork;
        this.status = ApplyStatus.PENDING;
    }

    public void updateStatus(ApplyStatus status) {
        this.status = status;
    }

    @Override
    public String toString() {
        return "UserVolunteerWork{" +
                "id=" + id +
                ", user=" + user +
                ", volunteerWork=" + volunteerWork +
                ", status=" + status +
                '}';
    }
}

해당 엔티티는 유저와 봉사활동 다대다 연관관계를 위한 중간 테이블이다.

VolunteerService

 @Transactional(timeout = 5)
    public synchronized void approve(String email, Long volunteerWorkId, Long userId) throws IllegalAccessException {

				// 문제 발생 첫번째 코드
        UserVolunteerWork userVolunteerWork = userVolunteerRepository
                .findByVolunteerWorkIdAndUserId(volunteerWorkId, userId)
                .orElseThrow(EntityNotFoundException::new);

        if (userVolunteerWork.getStatus() != ApplyStatus.PENDING) {
            throw new IllegalStateException("대기 상태가 아닙니다. userVolunteerWorkId =" + userVolunteerWork.getId());
        }

        VolunteerWork volunteerWork = userVolunteerWork.getVolunteerWork();
        Organization organization = userRepository.findByEmail(email).orElseThrow(EntityNotFoundException::new).getOrganization();

        // 승인권한은 해당 기관담당만 가능하도록 해야함.
        if (!volunteerWork.getOrganization().equals(organization)) {
            throw new AuthorizationFailedException(AUTHORIZATION_ERR_MSG);
        }

        if (volunteerWork.getAppliedParticipants().get() >= volunteerWork.getMaxParticipants()) {
            throw new IllegalAccessException();
        }

        User user = userVolunteerWork.getUser();
        user.attendVolunteerWork(userVolunteerWork);
        volunteerWork.attendUser(userVolunteerWork);
        volunteerWork.increaseParticipants();
        userVolunteerWork.updateStatus(ApplyStatus.APPROVED);
    }

UserVolunteerRepositoryCustomImpl