Search

[7/17] AOP와 Filter로 Soft Delete 기능 구현하기

Created time
2024/07/17 17:07
Tags
SpringBoot
프로젝트
14
pull

 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.*(..))") 로 수정할 예정.