soft delete는 database
의 테이블에서 물리적으로 데이터를 삭제하는 대신 마킹을 통해서 delete
를 구현하는 방식이다.
soft delete를 구현하는 가장 보편화된 방식은 삭제여부를 나타내는 field
를 추가하는 방식이다.
다음은 deleted
란 field
를 통해 삭제 여부를 관리하는 방식의 예시이다.
해당 테이블에서 다음과 같은 SQL
command를 통해 delete
작업을 수행할 수 있다.
update from post set deleted=1 where id=1
soft delete 방식을 도입하면 테이블 조회 방식도 바뀌게 된다.
select * from post where deleted=0
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@Builder
public class Post {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotNull
private String title;
@NotNull
private String content;
@NotNull
private String writer;
private boolean deleted = Boolean.FALSE;
}
위 코드를 보면 post
엔티티에 default
값을 false
로 둔 deleted
변수를 추가하여 soft delete를 구현한 것을 확인할 수 있다.
이런 방식으로 구현하면 삭제할 때마다 update
쿼리를 날려야 하고 조회 할때마다 where
절을 통해 deleted=false
인 데이터만 조회하도록 쿼리를 작성해야 한다.
다음과 같은 JPA repsitory
의 delete
command를 오버라이드하여 해당 작업을 간편화 할 수 있다.
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@Builder
@SQLDelete(sql = "UPDATE post SET deleted = true WHERE id=?") - (1)
@Where(clause = "deleted=false") - (2)
public class Post {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotNull
private String title;
@NotNull
private String content;
@NotNull
private String writer;
private boolean deleted = Boolean.FALSE;
public void update(String title, String content) {
this.title = title;
this. content = content;
}
}
(1) @SQLDelete
annotation을 통해 delete
command를 deleted=true
로 update
하는 SQL
command로 바꾼다.
(2) @Where
annotation을 통해 deleted=false
인 데이터만 조회하도록 한다.
public interface PostRepository extends JpaRepository<Post, Long> {
}
PostRepository
에는 특별한 변경점이 없다.
@RequiredArgsConstructor
@Service
public class PostService {
private final PostRepository postRepository;
public Long create(PostRequest postRequest) {
Post post = postRequest.toEntity();
return postRepository.save(post).getId();
}
public PostResponse getPost(long postId) {
Post findPost = findPostById(postId);
return PostResponse.builder()
.post(findPost)
.build();
}
public PostsResponse getPosts() {
List<Post> posts = postRepository.findAll();
return PostsResponse.builder()
.postResponses(posts.stream()
.map(post -> PostResponse.builder()
.post(post)
.build())
.collect(Collectors.toList()))
.build();
}
public Long update(Long postId,PostUpdateRequest postUpdateRequest) {
Post findPost = findPostById(postId);
findPost.update(postUpdateRequest.getTitle(), postUpdateRequest.getContent());
return postRepository.save(findPost).getId();
}
public void delete(long postId) {
postRepository.deleteById(postId);
}
private Post findPostById(long postId) {
return postRepository.findById(postId).orElseThrow(() -> new NotFoundException(ErrorCode.ENTITY_NOT_FOUND.getMessage()));
}
}
PostService
에도 특별한 변경사항 없이 원래 쓰던 deleteById
를 사용하면 update
쿼리로 변환되어 soft delete가 수행된다.
조회 메소드 또한 조회시 where
절에 deleted=false
가 추가되어 쿼리를 날리게 된다.
이런 기능이 있었군요~