JPA/ORM JPA

JPA auditing, baseEntity를 활용하여 Soft delete 구현

Lahezy 2023. 8. 9.
728x90

지난번 softdelete와 관련한 글을 작성한 적이 있습니다.

그때는 직접 필드를 생성해서 관리해 주는 방식으로 진행했는데 이번 미니 프로젝트에서는 baseEntity에 생성일, 수정일, 삭제일을 입력받도록 수정해 보았습니다.

 

BaseEntity 생성

먼저 baseEntity를 생성했습니다.

package com.lahee.market.entity;

import jakarta.persistence.Column;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.MappedSuperclass;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.time.Instant;

@Getter
@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
public abstract class BaseEntity {
    @Column(name = "created_at", updatable = false)
    @CreatedDate
    private Instant createdAt;

    @Column(name = "updated_at", updatable = false)
    @LastModifiedDate
    private Instant updatedAt;

    @Column(name = "deleted_at", updatable = false)
    private Instant deletedAt;

    @CreatedBy
    private String createdUser;

    @LastModifiedBy
    private String modifiedUser;
}

포인트는 @MappedSuperclass와 @EntityListeners입니다 관련 정보는 이전 JPA Auditing글을 참고하시면 좋을 것 같습니다.

2023.06.24 - [JPA] - JpaAuditing을 이용해서 base entity 생성하기

과거 글에서도 설명하였듯이 SpringSecurity를 활용한다면 각 아이템을 생성한 사람과 수정한 사람들의 정보를 입력받을 수 있습니다. 해당 부분은 아래와 같이 공식문서와 같은 방법으로 설정가능합니다.

 

공식 문서: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#auditing 

package com.lahee.market.config;

import org.springframework.data.domain.AuditorAware;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;

import java.util.Optional;

public class AuditorAwareImpl implements AuditorAware<String> {

    @Override
    public Optional<String> getCurrentAuditor() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        if (authentication == null || !authentication.isAuthenticated()
                || authentication.getPrincipal().equals("anonymousUser")) {
            return Optional.empty();
        }

        return Optional.of(authentication.getName());
    }
}

만약 해당 기능이 필요 없다면     

@CreatedBy  private String createdUser;  @LastModifiedBy private String modifiedUser;를 제거하면 됩니다. 

이런 경우 생성, 수정 시간만 자동으로 입력됩니다.

 

이후에 baseEntity를 상속받아서 사용하면 됩니다. 상속하여 사용한 경우 아래와 같이 칼럼이 자동으로 생성됩니다.

 

이번에는 soft delete를 수정해 보겠습니다.

관련 게시글은 2023.05.12 - [JPA] - JPA에서 soft delete 쉽게 처리하기을 참고하시면 좋을 것 같습니다.

JPA SoftDelete 적용

@NoArgsConstructor
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Getter
@Entity
@Table(name = "user")
@SQLDelete(sql = "UPDATE user SET deleted_at = CURRENT_TIMESTAMP WHERE id = ?")
@Where(clause = "deleted_at IS NULL")
public class User extends BaseEntity { //다음과 같이 상속 합니다.
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true)
    private String username; //loginId
    private String password;
    @Column(unique = true)
    private String nickname;
 }

코드를 설명하자면 만약 삭제 쿼리가 실행되는 경우 @SQLDelete에 의해 delete_at필드가 현재시간으로 업데이트됩니다. 

조회 시에는 해당 delete_at field가 null인 경우만 조회합니다.

 

참고로 만약 위에서 삭제 시에 아래와 같이 매핑된 관계에서 자식이 부모의 영향아래 있다면 연관된 테이블의 값은 실제로 삭제됩니다.

@OneToMany(fetch = LAZY, mappedBy = "salesItem", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Comment> comments = new ArrayList<>();

따라서 관계 매핑 엔티티가 물리적 삭제되지 않길 바라시면  cascade, orphanRemoval을 삭제하거나 아래와 같이 관계 테이블에도 softdelete를 적용하도록 해줄 수 있습니다.

 

다른 경우는 userId값을 null로 설정해 주어 관리하는 방법도 있다고 합니다.

저의 경우는 지금은 삭제 시에 관계 엔티티도 함께 소프트 딜리트가 진행될 수 있도록 수정하였습니다.

@Table(name = "comment")
@SQLDelete(sql = "UPDATE comment SET deleted_at = datetime('now') WHERE id = ?")
@Where(clause = "deleted_at IS NULL")
public class Comment extends BaseEntity

 

이렇게 해보니 쿼리문이 아주 길어진다는 단점이 있었습니다. 하지만 개인적으로는 JPA에서 간단하게 사용하기는 좋은 것 같아 해당방법을 사용하여 진행하였습니다. 

 

또한 참고로 softdelete(논리삭제)를 진행하는 테이블을 검색하는 경우 함상 deleted_at IS NULL을 함께 조회하기 때문에 만약 삭제된 것도 함께 조회하고 싶은 경우는 Filter나 JPQL 네이티브 쿼리를 사용하여 찾을 수 있습니다. 

728x90

'JPA > ORM JPA' 카테고리의 다른 글

JPA, H2 데이터 베이스 대소문자 구분에러  (1) 2023.05.12

댓글