1. κ°μ
κ°λ°μ νλ€λ³΄λ©΄ μ΄λ€ λ°μ΄ν°κ° μμ λλ©΄ μ°κ΄λ λ€μν λ°μ΄ν°λ€μ΄ ν¨κ» μμ λμΌ νλ κ²½μ°κ° μ’ μ’ λ°μνλ€.
μλ₯Ό λ€λ©΄ κ²μκΈμ΄ μμ λλ©΄ ν΄λΉ κ²μκΈμ λ¬λ¦° μ’μμ, λκΈ, λ΅λ³ λ±λ± μ°κ΄λ λ°μ΄ν°λ ν¨κ» μμ λμΌ νλ€.
νμ§λ§, ν¨κ» μμ λλ©΄ μ λλ κ²½μ°λ μλ€.
μλ₯Ό λ€μ΄, μν κ±°λ μλΉμ€μμ ν맀μμ μ 보λ₯Ό μμ νλ©΄ ν맀μκ° μ¬λ¦° μνμ λν μ 보λ μμ λ κ²μ΄λ€. κ·Έλ¬λ, ꡬ맀μλ μμ μ΄ κ΅¬λ§€νλ μνμ μ 보λ μμμ¦μ νμΈν΄μΌ ν νμκ° μμ μ μλ€. λ§μ½ ν맀μ μ 보μ ν¨κ» ν΄λΉ μν μ 보λ μμ λλ€λ©΄, ꡬ맀μλ μμ μ ꡬ맀 μνμ λν μ 보λ₯Ό νμΈν μ μκ² λμ΄ λΆνΈμ κ²ͺμ μ μλ€. λ°λΌμ μ΄λ¬ν κ²½μ°μλ ν맀μ μ 보μ μ°κ΄λ λ°μ΄ν°λΌλ, νΉμ 쑰건νμμ 보쑴λκ±°λ κ΄λ¦¬λμ΄μΌ νλ€.
μ§κΈκΉμ§ νλ‘μ νΈλ₯Ό μ§νν΄μ€λ©΄μ λ°μ΄ν° μμ μ΄λ»κ² νμ§? νλ κ³ λ―Όλ§ ν΄μ€λ€, μ΄λ² κΈ°νμ μ 리νκ³ λμ΄κ°λ € νλ€.
2. λ°μ΄ν° μμ λ°©λ²
λ°μ΄ν°λ₯Ό μμ νλλ΄ ν¬κ² 2κ°μ§ λ°©λ²μ΄ μλ€. Soft Delete(λ Όλ¦¬ μμ ), Hard Delete(물리 μμ ) λ°©μμ΄ μλλ° λ κ°μ§ λ°©μμ μ°¨μ΄λ μλμ κ°λ€.
- Soft Delete
λ°μ΄ν°λ² μ΄μ€μμ λ°μ΄ν°λ₯Ό μμ νμ§ μκ³ , μ¬μ©μ μ μ₯μμλ λ°μ΄ν°μ μ κ·Όν μ μκ² νλ λ°©μμ μλ―Ένλ€. λ³΄ν΅ ν μ΄λΈμ is_deleted컬λΌμ λ§λ€μ΄ booleanκ°μΌλ‘ λ°μ΄ν°λ₯Ό μ¬μ©μ¬λΆλ₯Ό κ²°μ νλ λ°©μμ΄λ€. is_delete = 0μ΄λ©΄ μ‘°ν κ°λ₯, is_deleted = 1μ΄λ©΄ μ‘°ν λΆκ°λ₯ν κ²μ²λΌ λ§μ΄λ€. 쑰건 컬λΌμ΄ λ€μ΄κ°λ―λ‘ is_deletedμ κ°μ 체ν¬νλ μΏΌλ¦¬κ° λ€μ΄κ°μΌ νλ€.
soft μμ μ μ¬μ©μλ λ°μ΄ν°κ° μμ λ κ²μ²λΌ ν΄λΉ λ°μ΄ν°μ μ κ·Ό λΆκ°λ₯νμ§λ§, μ ν리μΌμ΄μ DBμλ λ°μ΄ν°κ° μ¬μ ν μ‘΄μ¬νλ€. λλ¬Έμ λ΄λΆμμ λ°μ΄ν°λ₯Ό κ³μ μ¬μ©ν΄μΌν κ°λ₯μ±μ΄ μλ€λ©΄ soft delete λ°©μμ μ ννλ κ²μ΄ μ ν©ν μλ μλ€.
UPDATE User SET is_deleted = 1 where userId = ?
- Hard Delete
λ°μ΄ν°λ² μ΄μ€μμλ λ°μ΄ν°λ₯Ό μ§μ μμ νλ λ°©μμ μλ―Ένλ€. λμ΄μ μ¬μ©νμ§ μλ λ°μ΄ν°λ₯Ό DBμ μ μ₯νλ κ²μ μ μ₯곡κ°μ λλΉνλ κ²μΌ μ μλ€.
νν μ°λ¦¬κ° Delete 쿼리λ₯Ό λ 리λ κ²μ΄λ€.
DELETE FROM User WHERE userId = ?
μ€λ¬΄μμ κ²½μ°μ λ°λΌ λ€λ₯΄κ² μ§λ§ λ Όλ¦¬ μμ λ₯Ό λ§μ΄ νλ€κ³ νλ€.
λ€μν μ΄μ κ° μκ² μ§λ§
FKλ‘ μ¬μ©λλ λ°μ΄ν°μΈ κ²½μ°λ, 볡μμ΄ κ°λ₯ν΄μΌ νλ λλ©μΈμΌ κ²½μ° λ Όλ¦¬ μμ λ₯Ό ννλ€.
λν Update μΏΌλ¦¬κ° Delete μΏΌλ¦¬λ³΄λ€ ms λ¨μλ‘ λ΄€μ λ λ λΉ λ₯΄κΈ°λ νλ€.
λ§μ§λ§μΌλ‘ λ°μ΄ν°λ μμ°μ΄κΈ° λλ¬Έμ΄λ€. μ μ λ€μ΄ μλΉμ€λ₯Ό μ¬μ©νλ©΄μ μκ³ μλ λ§μ λ°μ΄ν°λ€μ λ²μ κ²½κ³λ₯Ό λ²μ΄λμ§ μλ μ μμ λ°μ΄ν° λΆμμ ν΅ν΄ λ§μΌν λ° κΈ°ν κ°λ°, λλ²κΉ λ± λ€μν λ°©λ©΄μμ μ¬μ©λλ€.
νμ§λ§, λ¨μ λν λͺ ννλ€.
λ°μ΄ν°κ° μμ±λκΈ°λ§ νκ³ μμ λμ§ μλ ꡬ쑰μ΄κΈ° λλ¬Έμ λ°μ΄ν°λ² μ΄μ€μ μ©λμ΄ λ§€μ° μ»€μ§ μ λ°μ μλ€. μ΄ λ°μ΄ν°λ€ μ€μλ μ λ§λ‘ νμ μλ λ°μ΄ν°λ€λ μμ μ μλ€.
λν, λ Όλ¦¬ μμ λ μ ν리μΌμ΄μ μ±λ₯ μΈ‘λ©΄μμ μκ°ν΄λ΄λ SELECT μ‘°ν μ λΆνμν κ²μ 쑰건μ μΆκ°ν΄μΌ νλ€.
SELECT νλ λͺ¨λ 쿼리μ WHERE μ μ μμ νμΈ μ¬λΆκ° λ€μ΄κ°λ€λ©΄ κ½€λ μ§κ΄μ μ΄μ§ μμ κ²μ΄λ€.
3. Springμμμ Soft Delete λ°©λ²λ€
- @SQLDelete
λ¨Όμ @SQLDeleteλ₯Ό μ¬μ©νμ§ μκ³ Soft Deleteλ₯Ό νλ €λ©΄ μλμ²λΌ νκ² λ κ²μ΄λ€.
@Entity
public class Board {
@Id
@GeneratedValue
private Long id;
private String title;
private String content;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
private boolean isDeleted = false;
}
public void deleteBoard(Long boardId) {
Optional<Board> findBoard = boardRepository.findById(boardId);
findBoard.ifPresent(board -> {
board.setDeleted(true);
});
}
μ λ°©μμ SQLDelete μ΄λ Έν μ΄μ μ μ¬μ©νλ©΄ μλ μ²λΌ λ³κ²½ν μ μλ€.
@Entity
@SQLDelete(sql = "UPDATE board SET is_deleted = true WHERE id = ?")
public class Board {
@Id
@GeneratedValue
private Long id;
private String title;
private String content;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
private boolean isDeleted = false;
}
public void deleteBoard(Long boardId) {
boardRepository.deleteById(boardId);
}
- @Where
μμμ Soft Delete μ SELECT νλ λͺ¨λ 쿼리μ WHERE μ μ μμ νμΈ μ¬λΆλ₯Ό νμΈν΄μΌνλ λ§€μ° λ²κ±°λ‘μ΄ λ¨μ μ΄ μμλ€.
@Where μ΄λ Έν μ΄μ μ μ΄λ° λΉμ§κ΄μ μΈ λ¬Έμ μ λ²κ±°λ‘μμ ν΄κ²°ν΄μ€ μ μλ€.
@Entity
@Where(clause = "is_deleted = false")
@SQLDelete(sql = "UPDATE board SET is_deleted = true WHERE id = ?")
public class Board {
@Id
@GeneratedValue
private Long id;
private String title;
private String content;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
private boolean isDeleted = false;
}
μμ κ°μ΄ @Where μ΄λ Έν μ΄μ μ μ¬μ©νλ©΄ Board μν°ν°λ₯Ό μ‘°νν λ νμ is_deleted = false μ‘°κ±΄μ΄ whereμ μ μΆκ°λλ€.
μ΄λ μν°ν° μ‘°νλΌλ 건
"select b from Board b" κ°μ΄ μν°ν° μ λΆλ₯Ό μ‘°ννλ κ²½μ°λ₯Ό λ§νλ€.
μ¦, "select b.id, b.title from Board b" κ°μ΄ μν°ν° μ μ²΄κ° μλ νΉμ νλλ§ μ‘°ννλ κ²½μ°μ @Where μ΄λ Έν μ΄μ μ λͺ μνλ μ‘°κ±΄μ΄ μΏΌλ¦¬μ λ°μλμ§ μλλ€.
* μ£Όμ
- μ΄λ Έν μ΄μ μ μ¬μ©λλ SQLλ¬Έμ SQLλ¬Έ κΈ°μ€μΌλ‘ μμ±ν΄μΌ νλ€. μλ₯Ό λ€μ΄ isDeleted κ° μλλΌ id_deletedλ‘ μμ±ν΄μΌ ν¨.
- hibernate, JPAλ± λΌμ΄λΈλ¬λ¦¬κ° μ€μΉλμ΄ μμ΄μΌ νλ€.
- @SQLDelete μ ? μλ ν΄λΉ μν°ν°μ @Id μ΄λ Έν μ΄μ μ μ¬μ©ν νλ‘νΌν°μ κ°μ΄ μ¬μ©λλ―λ‘, pkλ₯Ό idλΌλ 컬λΌμΌλ‘ μ¬μ©ν λ μμ κ°μ΄ μ¬μ©ν΄μΌ ν¨. μλλΌλ©΄ κ°μμ λ§κ² λ³κ²½.
4. νλ‘μ νΈμ μ μ©
μμ μκ°ν λ is_deleted κ°μ boolean νμμ νλλ₯Ό μ¬μ©νμ§λ§ νμ μ LocalDateTime νμ μ deleted_at νλλ₯Ό μ¬μ©νλ€.
λ¨μ μμ μΈμ§λ₯Ό λνλ΄κΈ° μν΄μ booleanμ μ¬μ©νλ©΄ λμ§λ§, μΆνμ λλ²κΉ μ μν΄ μμ μκ°μ κΈ°λ‘ν΄λμλ€. κΌ νμκ° μλ€λ©΄ booleanμ΄ DB μ‘°ν μ λ λΉ λ₯΄κΈ° λλ¬Έμ booleanμ μ¬μ©νλκ² λ μ’μ μλ μλ€.
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@SQLDelete(sql = "UPDATE job_post SET deleted_at = NOW() WHERE job_post_id = ?")
@Where(clause = "deleted_at IS NULL")
public class JobPost extends BaseEntity {
@Id
@GeneratedValue
@Column(name = "job_post_id")
private Long id;
// μλ΅
private LocalDateTime deletedAt; // λ
Όλ¦¬ μμ
public void deleteJobPost(Long companyId, Long jobPostId) {
Member company = memberRepository.findById(companyId)
.orElseThrow(() -> new JikgongException(ErrorCode.MEMBER_NOT_FOUND));
JobPost jobPost = jobPostRepository.findById(jobPostId)
.orElseThrow(() -> new JikgongException(ErrorCode.JOB_POST_NOT_FOUND));
List<WorkDate> workDateList = jobPost.getWorkDateList();
// WorkDate.getDate() κ° μ€ κ°μ₯ ν° λ μ§λ₯Ό μ°ΎκΈ°
LocalDate maxDate = findMaxDate(workDateList);
// μ€λ λ μ§
LocalDate today = LocalDate.now();
// 7μΌ μ΄μ μ§λ κ³΅κ³ : μμ
// μλ κ³΅κ³ : νμ λ μ§μμ μλ€λ©΄ μμ
validationBeforeDelete(today, maxDate, workDateList);
// μ‘°κ±΄μ΄ μΆ©μ‘±λλ©΄ λ
Όλ¦¬ μμ μ€ν
jobPostRepository.delete(jobPost);
}
μ΄λ κ² μ€μ ν΄λκ³ μμ μμ²μ 보λ΄λ©΄ μΏΌλ¦¬κ° μλμ κ°μ΄ λ¬λ€.
2024-08-22 19:16:05.727 [http-nio-8080-exec-4] INFO p6spy - [statement] | 40 ms |
delete
from
pickup
where
pickup_id=14
2024-08-22 19:16:05.776 [http-nio-8080-exec-4] INFO p6spy - [statement] | 46 ms |
delete
from
work_date
where
work_date_id=22
2024-08-22 19:16:05.826 [http-nio-8080-exec-4] INFO p6spy - [statement] | 48 ms |
delete
from
work_date
where
work_date_id=23
2024-08-22 19:16:05.884 [http-nio-8080-exec-4] INFO p6spy - [statement] | 56 ms |
delete
from
work_date
where
work_date_id=24
2024-08-22 19:16:05.979 [http-nio-8080-exec-4] INFO p6spy - [statement] | 93 ms |
UPDATE
job_post
SET
deleted_at = NOW()
WHERE
job_post_id = 8
Update 쿼리λ μ λκ°λλ° μ°κ΄κ΄κ³ λ§Ίμ΄μ§ workdate μ pickupμ DELETE μΏΌλ¦¬κ° λκ°λ κ±Έ νμΈν μ μμλ€.
cascade, orphanRemoval μ€μ κ³Ό @SQLDeleted λ₯Ό ν¨κ» μ¬μ©νλ€λ©΄ λΆλͺ¨ μν°ν°λ λ Όλ¦¬ μμ λμ§λ§ μμ μν°ν°λ κ°μ°¨μμ΄ DELETE μΏΌλ¦¬κ° λκ°λ€.
μμ μ μμλ‘ λͺ¨μ§κ³΅κ³ μμ λ‘μ§μ ꡬνν΄λμ λ ν¨κ» μμ νκΈ° μν΄ μλμ κ°μ΄ μ€μ ν΄λλλ°,, λλΆμ νλ λ°°μκ°λ€.. γ γ γ
cascade, orphanRemoval μ€μ μ κ±° νμ μλνλλλ‘ μΏΌλ¦¬κ° μ λκ°λ€.