Home

AOP와 Filter로 Soft Delete 기능 구현하기

 Soft Delete

데이터베이스에서 직접 데이터를 삭제하는 Hard Delete 방식의 반대
보통 Boolean 필드(컬럼)을 추가해서 해당 컬럼을 통해 삭제 여부를 판단한다.

 SpringBoot에서의 구현

import org.hibernate.annotations.*; @Entity @Getter @NoArgsConstructor @Table(name = "account") @SQLDelete(sql = "UPDATE account SET is_deleted = true WHERE id = ?") @FilterDef(name = "deletedAccountFilter") @Filter(name = "deletedAccountFilter", condition = "is_deleted = false") public class AccountEntity extends BaseEntity { ... @ColumnDefault("0") @Column(name = "is_deleted", nullable = false) private Boolean isDeleted; }
Java
복사
원래는 hibernate의 @Where 어노테이션을 사용해서 구현했는데, deprecated라고 해서 @Filter 를 사용하였다.

@SQLDelete

@SQLDelete(sql = "UPDATE account SET is_deleted = true WHERE id = ?") => public void remove(Account account) { accountRepository.delete( accountRepository.findById(account.getId()).orElseThrow() ); } => UPDATE account SET is_deleted = true WHERE id = ?
Java
복사
JPARepository에서 delete 메소드를 호출할 때, DELETE 쿼리가 아닌 SQLDelete 어노테이션 안의 sql에 정의된 쿼리(is_delete를 true로)를 실행시킨다.

@Filter

@FilterDef(name = "deletedAccountFilter") @Filter(name = "deletedAccountFilter", condition = "is_deleted = false") --- Specifies that an entity or collection is affected by a named filter declared using FilterDef, and allows the default filter condition to be overridden for the annotated entity or collection role. For example, we might apply a filter named Current to an entity like this: @Entity @Filter(name = "Current", deduceAliasInjectionPoints = false, condition = "{alias}.year = extract(year from current_date)") class Course { @Id @GeneratedValue Long id; int year; ... }
Java
복사
엔티티에 조건을 지정하고 조건에 맞추어 쿼리를 실행시키는 역할을 한다. 다만 쿼리를 실행하기 전에 엔티티 매니저에서 Session을 통해 Filter를 활성화해야 한다.

HibernateFilterAspect

@Aspect @Component @RequiredArgsConstructor public class HibernateFilterAspect { private final EntityManager entityManager; @Before("execution(* com.berry.next.*.application.*.*(..))") public void enableHibernateFilter() { Session session = entityManager.unwrap(Session.class); session.enableFilter("deletedAccountFilter"); } }
Java
복사
서비스 레이어에서 구현해야 할 내용은 아니라고 생각해서 AOP를 통해서 구현하였다.
후에 @Before("execution(* com.berry.next.*.application.service.*(..))") 로 수정할 예정.