-
Quartz를 이용하여 softDelete한 내용을 실제로 delete 하기🌱 spring/🚛 spring batch 2022. 12. 28. 18:52
이번에는 Quartz를 이용하여 이전 프로젝트에서 사용해봤던 softDelete를 한 데이터에 대해 특정 시간이 지났을 경우 실제로 데이터를 삭제(hard delete)하는 작업을 해보았다.
softDelete를 하기 위해 예제로 user entity를 만들어 사용했다.
User.java
@Entity @Table(name = "user") @Where(clause = "is_deleted = false") @SQLDelete(sql = "UPDATE user SET is_deleted = true WHERE id = ?") @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor @Getter public class User extends BaseEntity { @Id @GeneratedValue private Long id; @Column(name = "name") private String name; @Column(name = "age") private Integer age; @Column(name = "is_deleted") private Boolean isDeleted; public User(String name, Integer age) { this.name = name; this.age = age; isDeleted = Boolean.FALSE; } }
- 삭제를 할 경우 SQLDelete로 인해 실제 데이터가 삭제되지 않고 isDeleted 가 true로 update 된다.
이렇게 isDeleted가 true가 된 데이터들을 실제로 삭제하는 작업을 하기 위해
UserRepository interface에서 isDeleted 된 유저정보들을 가져오는 함수와 실제로 삭제하는 함수를 native query를 통해 생성해주었다.
UserRepository
public interface UserRepository extends JpaRepository<User, Long> { @Query(value = "SELECT * FROM user AS u WHERE u.is_deleted = true", nativeQuery = true) List<User> findDeleteUserList(); @Modifying @Query(value = "DELETE FROM user AS u WHERE u.is_deleted = true AND u.id = :userId", nativeQuery = true) void hardDelete(@Param("userId") Long userId); }
⚠️ 주의
- @Modifying 어노테이션을 작성해주어야 한다!!
- native query를 통해 작성된 INSERT, UPDATE, DELETE 쿼리에서는 사용해주어야 한다.
- SELECT 제외
- 이 어노테이션을 작성하지 않아서 다음과 같은 에러가 계속 발생했었다.
Job
userService.java
@Transactional public void deleteStep(LocalDateTime now){ List<User> userByIsDeleted = userRepository.findDeleteUserList(); List<User> deleteUsers = userByIsDeleted.stream() .filter(user -> gapDate(user.getUpdatedAt(), now)) .collect(Collectors.toList()); for (User deleteUser : deleteUsers) { userRepository.hardDelete(deleteUser.getId()); } } private boolean gapDate(LocalDateTime updateDate, LocalDateTime now) { Duration between = Duration.between(updateDate, now); if(between.getSeconds() > 120) return true; return false; }
- 예제를 위해서 해당 시간과 삭제된(updated된) 시간이 2분이상 차이가 날 경우 hardDelete하도록 하였다.
Job 생성
Controller
@PostMapping("/jobs") public ResponseEntity<?> addScheduleJob(@RequestBody JobRequest jobRequest) throws SchedulerException { JobKey jobKey = new JobKey(jobRequest.getJobName(), jobRequest.getJobGroup()); if(!schedulerServiceImpl.isJobExist(jobKey)) { schedulerServiceImpl.addJob(jobRequest, DbCronJob.class); } else { return new ResponseEntity<>(new ApiResponse(false, "Job already exists"), HttpStatus.CONFLICT); } return new ResponseEntity<>(new ApiResponse(true, "Job created Successfully"), HttpStatus.CREATED); }
- 이미 있는 job이 아니라면 Job 생성
DbCronJob
public class DbCronJob extends QuartzJobBean { private static Logger log = LoggerFactory.getLogger(DbCronJob.class); @Autowired private UserService userService; @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { JobKey jobKey = context.getJobDetail().getKey(); JobDataMap jobDataMap = context.getJobDetail().getJobDataMap(); LocalDateTime now = LocalDateTime.now(); // 현재 시간을 기반으로 삭제된지 지정한 시간이 지난 데이터 삭제하기 userService.deleteStep(now); } }
SchedulerService
public boolean addJob(JobRequest jobRequest, Class<? extends Job> jobClass) { JobKey jobKey = null; JobDetail jobDetail; Trigger trigger; try { if(jobClass.equals(CronJob.class) || jobClass.equals(DbCronJob.class)) { trigger = JobUtils.createCronTrigger(jobRequest); } else trigger = JobUtils.createTrigger(jobRequest); jobDetail = JobUtils.createJobDetail(jobRequest, jobClass); jobKey = JobKey.jobKey(jobRequest.getJobName(), jobRequest.getJobGroup()); Scheduler scheduler = schedulerFactoryBean.getScheduler(); Date dt = scheduler.scheduleJob(jobDetail, trigger); log.debug("Job with jobKey : {} scheduled successfully at date : {}", jobDetail.getKey(), dt); return true; } catch (SchedulerException e) { log.error("error occurred while scheduling with jobKey : {}", jobKey, e); } return false; }
JobUtils
public static CronTrigger createCronTrigger(JobRequest jobRequest) { CronTrigger trigger = newTrigger() .withIdentity(jobRequest.getJobName(), jobRequest.getJobGroup()) .startNow() .withSchedule(CronScheduleBuilder.cronSchedule("0 0/1 18 * * ?")) .build(); return trigger; }
- Job의 실행을 trigger 한다.
- cron trigger를 생성해 schedule이 언제 실행되어야 하는지 설정해주고 주었다.
- 매일 18시에 1분 간격(19시까지)으로 실행하도록 했다.
테스트
유저 생성
유저 삭제(soft delete)
- is_deleted = true 가 되었다.
Quartz를 통한 hard delete
Job 추가 후 1분 후
- 아직 삭제 쿼리가 발생하지 않았다.
2분후
- DELETE 쿼리가 발생
- 데이터가 삭제되었다.
참고자료
'🌱 spring > 🚛 spring batch' 카테고리의 다른 글
Quartz2 (0) 2022.12.22 Quartz (1) (0) 2022.12.22 Spring Batch - ItemProcessor (0) 2022.12.16 Spring Batch - ItemWriter (0) 2022.12.16 SpringBatch - ItemReader (0) 2022.12.16