회원정보같은 건 탈퇴한다고 해서 db에서 바로 지우지 않기도 한다.
나중에 탈퇴한 회원을 복구해달라는 요청이 올 수 있기 때문이다.
소프트 딜리트는 다음과 같이 구현했다.
@SQLDelete(sql = "UPDATE accommodation SET is_deleted = true WHERE id =?")
@FilterDef(
name = "delete",
parameters = @ParamDef(name ="isDeleted", type = org.hibernate.type.descriptor.java.BooleanJavaType.class)
)
@Filter(
name="delete",
condition = "is_deleted = :isDeleted")
public class Accommodation {
(중략)
private Boolean isDeleted = false;
@Component
@RequiredArgsConstructor
public class FilterManger {
private final EntityManager entityManager;
public void enableFilter(String filterName, String paramName, Object paramValue) {
Session session = entityManager.unwrap(Session.class);
Filter filter = session.enableFilter(filterName);
filter.setParameter(paramName, paramValue);
}
public void disableFilter(String filterName) {
Session session = entityManager.unwrap(Session.class);
session.disableFilter(filterName);
}
}
public class FilterConstants {
public static String ACCOMMODATION_FILTER = "delete";
public static String FILTER_PARAM = "isDeleted";
}
@Transactional(readOnly = true)
public List<Accommodation> getAllAccommodation(){
filterManger.enableFilter(FilterConstants.ACCOMMODATION_FILTER
, FilterConstants.FILTER_PARAM
, false);
List<Accommodation> accommodations = accommodationRepository.findAll();
filterManger.disableFilter(FilterConstants.ACCOMMODATION_FILTER);
return accommodations;
}
소프트 딜리트를 할 때는 @SQLDelete, @Where 이 두가지 어노테이션을 많이 쓴다.
@SQLDelete는 delete 명령을 오버라이드 하는 어노테이션이다.
@Where는 어떤 데이터를 조회할지에 대한 조건을 걸 수 있다.
인텔리제이는 @Where가 곧 deprecated된다는 표시를 해준다.
Note that @Where restrictions are always applied and cannot be disabled. Nor may they be parameterized. They're therefore much less flexible than filters.
필터를 해제하는 게 불가능하고, 파라미터를 넣을수도 없어서 Filter보다 유연하지 않다고 한다.
영한님의 인프런 답변을 보면, 실제로 @Where를 쓰기 애매한 경우가 있다고 한다.
filter를 통해서 is_deleted가 true이거나 false인 상황을 모두 가져올 수 있었다. 파라미터를 적용하지 못한다면 이렇게 소프트 딜리트된 레코드를 가져오지 못할 것이다.
다만, 이 방법에는 문제가 있다.
Hibernate:
select
a1_0.id,
a1_0.accommodation_type,
a1_0.address,
a1_0.amenity,
a1_0.bed_count,
a1_0.description,
a1_0.host_id,
a1_0.is_deleted,
a1_0.is_on_sale,
a1_0.max_capacity,
a1_0.name,
a1_0.price,
a1_0.room_count
from
accommodation a1_0
where
a1_0.id=?
Hibernate:
select
l1_0.accommodation_id,
l1_0.id,
l1_0.user_id
from
likes l1_0
where
l1_0.accommodation_id=?
Hibernate:
select
p1_0.accommodation_id,
p1_0.id,
p1_0.is_deleted,
p1_0.url
from
picture p1_0
where
p1_0.accommodation_id=?
Hibernate:
select
sc1_0.accommodation_id,
sc1_0.id,
sc1_0.fee,
sc1_0.interval_days,
sc1_0.service_type
from
service_charge sc1_0
where
sc1_0.accommodation_id=?
Hibernate:
UPDATE
picture
SET
is_deleted = true
WHERE
id =?
Hibernate:
UPDATE
accommodation
SET
is_deleted = true
WHERE
id =?
연관관계가 있는 경우 위처럼 모두다 select를 해서 update를 해줘야 한다. 즉, 연관관계가 많아질 수록 sql 쿼리가 많이 나가는 거고 그만큼 성능에 영향을 줄 수 밖에 없을 것이다.
뭔가 마땅한 해결법을 찾기 어려웠는데
이런 내용을 발견했다. 지금 요구사항에서는 삭제를 한다고 하면 보통 숙소를 단건으로 삭제한다. 그렇다면, 연관관계에 의한 sql 쿼리가 많아봤자 20개 미만 정도다. 그러면 꼭 이걸 다른 방식으로 해결해야 할까? 이대로 놔두더라도 괜찮을 거 같다는 판단이 든다!
이 부분은 나중에 n+1 문제 해결을 하면서 공부하자.