0. 들어가며
데이터베이스에서 데이터를 삭제하는 방법으로는 물리 삭제(Hard Delete)와 논리 삭제(Soft Delete)가 있다.
Hard Delete는 DELETE 쿼리를 수행해 데이터베이스에서 데이터를 영구적으로 삭제하는 방법이다.
Soft Delete는 UPDATE 쿼리를 수행해 레코드의 활성 여부를 나타내는 필드를 비활성으로 수정하는 방법이다.
Soft Delete는 데이터가 유지되기 때문에 데이터 복원에 유리하다. 하지만, 데이터가 지속적으로 쌓이기 때문에 데이터베이스의 용량이 커질 수 있으며, SELECT 쿼리를 이용한 데이터 조회 시 활성 여부를 체크하는 검색 조건이 추가되어야 한다.
1. How to Implement Soft Delete with Hibernate
Hibernate 6.4에서 Soft Delete을 위한 기능이 도입됐다. @SoftDelete 어노테이션을 추가함으로써 Hibernate는 레코드를 Soft Delete하기 위한 UPDATE 쿼리를 생성하고, Soft Delete된 레코드를 제외하도록 SELECT 쿼리문에 검색 조건을 추가한다.
1.1. @SoftDelete Annotation
@Target({ElementType.PACKAGE, ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Incubating
public @interface SoftDelete {
String columnName() default "";
SoftDeleteType strategy() default SoftDeleteType.DELETED;
Class<? extends AttributeConverter<Boolean, ?>> converter() default UnspecifiedConversion.class;
public interface UnspecifiedConversion extends AttributeConverter<Boolean, Object> {
}
}
@SoftDelete은 아래와 같은 속성을 가진다.
- strategy
데이터베이스에 값을 읽고 쓰는 데 사용할 전략
- SoftDeleteType.ACTIVE
활성된 레코드를 true로 표기하고, 기본 컬럼명으로 'active' 사용 - SoftDeleteType.DELETED (default)
비활성된 레코드를 true로 표기하고, 기본 컬럼명으로 'deleted' 사용
- columnName (Optional)
컬럼명 지정 - converter (Optional)
Hibernate의 기본 Soft Delete 구현은 레코드의 활성 여부를 boolean 타입으로 표기한다. 데이터베이스에 저장할 적절한 값을 결정하는 데 적용할 컨버터 지정하여 활성 여부 표기법을 변경할 수 있다.
- YesNoConverter.class
활성 여부 Y/N로 표기 - TrueFalseConverter.class
활성 여부 True/False로 표기 - NumericBooleanConverter.class
활성 여부 1/0로 표기 - Custom Converter
AttributeConverter<X, Y>를 구현한 Custom Converter를 통해 레코드의 활성 여부 표기법을 커스터마이징 할 수 있다.
단, 활성 여부 표기 필드는 길이가 1인 문자열이므로 활성 여부를 길이가 2 이상인 문자열로 활성 여부를 표기하고 싶다면 다른 방법을 이용해야 한다.
2. Example
@Getter
@Builder
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@SoftDelete
@Entity
public class Member {
@Id
@GeneratedValue
private Long id;
private String name;
}
@SoftDelete 어노테이션이 추가된 Member 엔티티를 생성하면, deleted 필드가 false로 초기화되는 것을 볼 수 있다.
해당 엔티티를 삭제하면 DELETE 쿼리가 아닌 UPDATE 쿼리가 수행되며, 전체 엔티티 조회 시 검색 조건에 deleted=false가 추가된다.
3. 마치며
Hibernate 6.3 까지는 Soft Delete를 하기 위해서 @SQLDelete와 @SQLRestriction(@Where이 deprecated된 이후 사용)를 사용했다.
두 어노테이션은 속성값으로 쿼리문과 조건을 직접 지정해주어야 하는 번거로움이 존재했다.
@Getter
@Builder
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@SQLDelete(sql = "UPDATE member SET deleted=true WHERE id=?")
@SQLRestriction(value = "deleted=false")
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private boolean deleted; // 활성 여부 필드
}
Hibernate 6.4 이후 제공되는 @SoftDelete 어노테이션을 사용하면, 이전보다 손쉽게 Soft Delete를 구현할 수 있다.
@Getter
@Builder
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@SoftDelete // Soft Delete 구현을 위한 코드 단순화
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// 활성 여부 필드 불필요
}
4. 소스 코드
https://github.com/jeongyuneo/blog-example/tree/main/soft-delete
blog-example/soft-delete at main · jeongyuneo/blog-example
Contribute to jeongyuneo/blog-example development by creating an account on GitHub.
github.com
References
'study > JPA' 카테고리의 다른 글
[Spring Data JPA] JPA Auditing을 이용한 생성/수정 이력 추적 (1) | 2024.07.03 |
---|---|
[JPA] Join vs Fetch Join (0) | 2024.01.21 |
[Hibernate] 하이버네이트 명명 전략(Hibernate Naming Strategy) (0) | 2022.09.22 |