DB 공통 컬럼 JPA 에서 사용_@MappedSuperclass, @SQLDelete

wooSim·2023년 7월 4일
0
post-thumbnail

1. Database 공통 컬럼이란?

실무에서 개발을하면 테이블마다 공통적으로 들어가는 컬럼들이 있습니다. 컬럼 명은 다를 수 있지만 같은 의미로 대표적으로 등록일시, 수정일시, 등록자, 수정자, 삭제여부 컬럼 등이 있을 것입니다. 그럼 해당 컬럼이 왜 모든 테이블에 들어갈까요??

  • 등록일시, 등록자, 수정자, 수정일시의 경우 이력관리와 유지보수를 위해 모든 테이블에 같은 이름의 컬럼으로 필수로 들어가는 컬럼이라고 볼 수 있습니다.

  • 삭제여부 컬럼은 데이터를 삭제하지 않고 삭제 flag를 변경하는 방법으로 Soft Delete라고 불립니다. 실제로 서비스를 구현할 때는 데이터 분석이나 복구의 용이성 등의 이유로 물리적으로 데이터를 삭제하는 Hard Delete 보다는 Soft Delete를 자주 사용합니다.


그렇다면 DB 접근 기술 중 하나인 JPA 에서는 이를 어떻게 적용하는지 알아보겠습니다.



2. @MappedSuperclass를 통한 Entity 상속

□ 등록일시, 수정일시

등록일시, 수정일시, 등록자, 수정자의 경우 @MappedSuperclass로 공통 Entity를 선언하고 각각의 Entity에 상속받아 사용할 수 있습니다.

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {

    @Column(updatable = false)
    private LocalDateTime createdDate;
    @Column
    private LocalDateTime modifiedDate;

    @PrePersist
    public void prePersist() {
        LocalDateTime now = LocalDateTime.now();
        createdDate = now;
        modifiedDate = now;
    }

    @PreUpdate
    public void preUpdate() {
        modifiedDate = LocalDateTime.now();
    }

}

먼저 해당 코드의 어노테이션을 설명하면

  • @MappedSuperclass : Enitity 마다 공통된 필드를 사용하기 위해 사용하는 어노테이션
  • @PrePersist : JPA 엔티티(Entity)가 비영속 상태에서 영속(persist)상태가 되는 시점 이전에 실행
  • @PreUpdate : 영속 상태의 엔티티를 이용하여 데이터 업데이트를 수행하기 이전에 실행
  • @EntityListeners(AuditingEntityListener.class) : Spring Data JPA에서 Entity의 상태를 감시(Audit)하기위해 Auditing 기능 포함

그리고 JPA Auditing 어노테이션을 모두 활성화할 수 있도록 JpaConfig 설정을 해줍니다.

@Configuration
@EnableJpaAuditing
public class JpaConfig {
}

Application 클래스에 @EnableJpaAuditing을 붙이는 경우가 있는데 @WebMvcTest를 활용한 코드에서 에러가 발생할 수 있기 때문에 @configuration을 분리하였습니다.


이제 각 Entity 클래스에서 extends 키워드로 상속받으면 됩니다.
@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Builder
@AllArgsConstructor
public class Ledger extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 20)
    private String userId;

    @Column(nullable = false, length = 8)
    private String useDate;
    ...

데이터 등록 및 수정 시 등록일시와 수정일시가 자동으로 들어가는 것을 확인할 수 있습니다.

참고로 BaseEntity 클래스의 경우 @CreatedDate, @LastModifiedDate 애너테이션을 통해 간단하게 작성할 수 있습니다.

@CreatedDate
@Column(updatable = false)
private LocalDateTime createdDate;

@LastModifiedDate
@Column
private LocalDateTime modifiedDate;

□ 등록자, 수정자

등록자와 수정자 필드도 마찬가지 입니다. JPA Auditing 기능과 @CreatedBy, @LastModifiedBy 어노테이션을 사용하여, 데이터가 생성되거나 수정될 때 Session에 저장된 ID 정보를 DB에 저장되게 기능을 구현해보겠습니다.
등록, 수정일시 필드와 같이 BaseEntity 클래스에 추가 해줍니다.

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {

    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createdDate;

    @LastModifiedDate
    @Column
    private LocalDateTime modifiedDate;

    @CreatedBy
    @Column(updatable = false)
    private String createdBy;

    @LastModifiedBy
    @Column
    private String updatedBy;
}

만약 Entity의 값이 생성 되고 변경 될 때 누가 등록했고 누가 수정했는지 까지 자동으로 값을 업데이트 해주는 기능을 구현할려면 AuditorAware 기능을 사용해야합니다.
AuditorAware을 implements 하여 데이터가 등록되고 수정될 때 Session의 ID가 자동으로 들어가도록 하겠습니다.

@RequiredArgsConstructor
@Component
public class LoginUserAuditorAware implements AuditorAware<String> {

    private final HttpSession httpSession;

    @Override
    public Optional<String> getCurrentAuditor() {

        SessionUser user = (SessionUser) httpSession.getAttribute("user");
        if(user == null)
            return null;

        return Optional.ofNullable(user.getUserId());
    }
}

여기까지 구현하고 H2 DB에서 확인하면 로그인 ID 정보가 자동으로 입력된 것을 확인하실 수 있습니다.



3. @SQLDelete을 이용한 Soft Delete 구현

먼저 Soft Delete를 구현하기 위해서는 Entity 에 삭제 여부를 판단할 flag 필드를 둬야합니다. 아래 코드에서는 deleted 필드를 추가해줬습니다.

@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Builder
@AllArgsConstructor
public class Ledger extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 20)
    private String userId;
    
    ...
    
	@Column
    private String cntn;

	// 삭제 flag 필드..boolean default 값은 false이다.
    @Column
    private boolean deleted;

}

Soft Delete를 구현하기 위해서는 2가지를 해줘야 합니다. 첫 번째로 삭제 로직이 수행되면 delete 쿼리 대신 update 쿼리로 deleted 필드값만 true로 변경시켜줘야 합니다. 두 번째로 이를 위해 두가지 어노테이션을 추가할 예정입니다. 그리고 조회 요청시 삭제 처리 되지 않은 데이터만 조회해야 하기에 where 절에 deleted = false 구문은 항상 들어가야 합니다. 위의 2가지를 구현하기 위해 다음 어노테이션을 추가해야합니다.

  • @SQLDelete : 엔티티 삭제가 발생했을 때 delete 쿼리 대신 실행시켜줄 커스텀 SQL을 작성할 수 있는 어노테이션
  • @Where : 기본적으로 적용할 where 구문을 뜻하는 어노테이션입니다.

위 2개의 어노테이션을 적용한 Entity 클래스 입니다.

@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Builder
@AllArgsConstructor
@SQLDelete(sql = "UPDATE Ledger l SET l.deleted = true WHERE l.id = ?")
@Where(clause = "deleted = false")
public class Ledger extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 20)
    private String userId;
    
    ...
    
	@Column
    private String cntn;

	// 삭제 flag 필드..boolean default 값은 false이다.
    @Column
    private boolean deleted;

}




이것으로 DB 테이블 설계 시 항상 들어가는 공통적인 컬럼인 등록일시, 수정일시, 등록자, 수정자, 삭제여부를 JPA로 구현해봤습니다. JPA를 공부하며 알게된 내용을 하나씩 정리 중이라 부족한 부분이 많을텐데 읽어주셔서 감사합니다!! 혹시 틀린 내용이나 코드가 있을 경우 언제든지 말씀해주시면 감사하겠습니다!!

profile
daily study

0개의 댓글