λ°μ΄ν°λ λ―Έλμ μμ μ΄λ€
λ°μ΄ν°λ νμ¬ λ§€μ° μ€μν μμμ΄λ€. νΉνλ νλμλ μΈκ³΅μ§λ₯μ νμ΅μν€λ λ° μμ΄μ λ°μ΄ν°λ μμ μλ κ°μ κ°μΉλ₯Ό μ§λκΈ°μ ν¨λΆλ‘ μμ νλ κ²μ λμ λ μλ€ λ²λ¦¬λ κ²κ³Ό κ°μ΅λλ€. μ°¨λΌλ¦¬ λ μ λ¨μ΄μ‘μΌλ©΄ λκ° κ°μ Έκ°μ λ§μλκ±°λΌλ μ¬λ¨Ήμ§ μμ λ μμ λΆνμ°λ μ λ? λΌκ³ μκ°λλ€.π₯π₯π₯
νμ§λ§ λ°μ΄ν°λ₯Ό μμ νλ κ²μ κ±°λΆν΄μλ μ λλ€. κ³ κ°μ΄ μμ μ λ°μ΄ν°λ₯Ό μμ νκΈΈ μν λ μ΄λ₯Ό κ±°λΆνλ©΄ ν΄λΉ μλΉμ€λ₯Ό λ μ΄μ μ΄μ©νμ§ μμ κ²μ΄λ€. κ·Έλμ μ°λ¦¬λ μ€μ λ‘ λ°μ΄ν°λ₯Ό μμ νλ κ²μ²λΌ 보μ΄λ λ°©μμΌλ‘ μ²λ¦¬νλ Soft Delete
λ₯Ό μ¬μ©ν κ² μ΄λ€.
물리μ μμ λΌκ³ λ νλλ° μ°λ¦¬κ° νμμ SQLλ¬Έμ μ¬μ©ν λ DELETE λͺ λ Ήλ¬Έμ΄ κ·Έ μμλ€.
DELETE FROM TABLE WHERE CONDITION = ?
ν΄λΉ 쿼리λ₯Ό μ€ννλ©΄ DBμμ μμ ν μμ νλκ²μ΄λ€. 곡κ°μ ν보νκ±°λ μ±λ₯ κ°μ μ μν΄ μ¬μ©λλ©° 보μ μ μ±
λ° κ°μΈμ 보 λ³΄νΈ λ²λ₯ λ±μ λ°λΌ μ¬μ©λ μ μλ€.
λ°μ΄ν°λ₯Ό μꡬν μμ νκΈ°μ μ μ€νκ² κΈ°λ₯μ λ§λ€μ΄μΌνλ©° μꡬ μμ μ λ°μ΄ν°λ₯Ό λ°±μ
λ±μ 보μ μμλ₯Ό μΆκ°λ‘ ꡬμ±ν΄μΌ νλ€.
λ§μ½ μ΄λ€ μ
μ± μ μ κ° κ²μλ¬Όμ λλ°°λ₯Ό νκ³ μλλ¬Όλ±μ μ¬λ¦¬λ λ±μ νμλ₯Ό νμμλ μΆμ μ νΌνκΈ° μν΄ κ²μλ¬Όμ μμ νλ©΄ μ΄λ»κ² λ κΉ?
ν΄λΉ κ²μλ¬Όμ μ‘°μ¬ν λ μ μΆμ νκΈ° μ΄λ €μΈκ²μ΄λ€. 그리νμ¬ μ¬μ©νλ λ°©μμ΄ Soft Delete
μ΄λ€.
λ
Όλ¦¬μ μμ λΌκ³ λ νλ©° λ μ½λλ₯Ό μ κ±°νλ λμ μμ λμλ€λ νμλ₯Ό λ¨κΈ°λ λ°©λ²μ΄λ€.
DBμλ μ¬μ ν λ¨μ μμΌλ μ‘°νλ₯Ό ν λ λ μ΄μ μμ λ λ°μ΄νΈλ₯Ό μ‘°ννμ§ μλλ€.
λ°μ΄ν° λ³΄μ‘΄μ΄ κ°λ₯νλ©° λ³΅κ΅¬κ° κ°λ₯νκ³ μμ λ λ°μ΄ν° μ΄λ ₯μ μΆμ μ΄ κ°λ₯νλ€λ μ₯μ μ΄ μλ€.
νμλμλ€λ λ°©λ²μ 2κ°μ§λ‘ μκ°ν μ μλ€.
λ¨Όμ μμ λμλ€κ³ 체ν¬νλ λ°©μμΌλ‘ deleted
컬λΌμ μ§μ νμ¬ μμ μ μ
λ°μ΄νΈλ₯Ό νλ λ°©λ²μ΄λ€
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
private boolean deleted; // μμ μ¬λΆλ₯Ό λνλ΄λ νλ
}
User user = userRepository.findById(userId);
user.setDeleted(true); // μμ νλ μ
λ°μ΄νΈ
userRepository.save(user);
λ€μμΌλ‘ deleted_at
컬λΌμ μ§μ νμ¬ μμ μ μλμΌλ‘ μμ λ λ μ§κ° μ
λ°μ΄νΈ λλ λ°©μμ΄λ€
λ¨Όμ Timestamped μΆμν΄λμ€λ‘ λ±λ‘νλ€. μ¬κΈ°μ μμ λ λ μ
λ°μ΄νΈλ₯Ό ν μ»¬λΌ deleted_at
μ μΆκ°νλ€.
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class Timestamped {
@CreatedDate
@Column(updatable = false)
@Temporal(TemporalType.TIMESTAMP)
private LocalDateTime createAt;
@LastModifiedDate
@Column
@Temporal(TemporalType.TIMESTAMP)
private LocalDateTime modifiedAt;
@Column
@Temporal(TemporalType.TIMESTAMP)
private LocalDateTime deletedAt; // μμ λ λ μλμΌλ‘ μ
λ°μ΄νΈ
}
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@SQLDelete(sql = "UPDATE users SET deleted_at=CURRENT_TIMESTAMP where id=?")
@Where(clause = "deleted_at IS NULL")
@Table(name = "users")
public class User extends Timestamped {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true, nullable = false)
private String email;
@Column(nullable = false)
private String password;
}
λ°μ΄ν°λ² μ΄μ€μμ νΉμ λ μ½λλ₯Ό μμ ν μ κ±°νλ SQL λ¬Έμ₯μ΄λ€.
μμ μ νΉμ 쑰건μ λ§λ ν΄λΉ SQLμ μ€ννλ 문ꡬμ΄λ€.
μ΄ μΏΌλ¦¬λ users
ν
μ΄λΈμμ νΉμ id
λ₯Ό κ°μ§ μ¬μ©μκ° μμ λ μ deleted_at
νλλ₯Ό νμ¬ μκ°μΌλ‘ μ€μ νμ¬ ν΄λΉ μ¬μ©μλ₯Ό μννΈ μμ νλ μμ
μ μννλ€.
νΉμ 쑰건μ μΆ©μ‘±νλ 쿼리 κ²°κ³Όλ₯Ό κ°μ Έμ€λλ‘ νν°λ§νλλ° μ¬μ©λλ€.
ν΄λΉ μ½λμμ @Where(clause = "deleted_at IS NULL")
λ deleted_atκ° NULLμ΄ μλλ μ¦ μμ λμ μ΄ μλ μν°ν°λ§ κ°μ ΈμλΌλ λ»μ΄λ€.
κ²°κ³Όλ₯Ό 보면 deleted_atμ μμ λ νμ¬ λ μ§κ° μ
λ°μ΄νΈκ° λμλ€.
@Where(clause = "deleted_at = null") μ²λΌ μ¬μ©ν΄μλ μλλ€. μ²μμ μ΄κ² λλ¬Έμ λ§μ κ³ λ―Όμ νμκ³ NULLμΌλ 체νΉνλ IS NULLμ μ¬μ©νμ¬ κ²μ¦μ ν΄μΌνλ€. λ§μ½ = nullλ‘ deleted_at = nullλ‘ μμ±μ λ€μκ³Ό κ°μ μΏΌλ¦¬λ¬Έμ΄ μ€νλμ΄ κ²°κ΅ μ무κ²λ κ°μ Έμ€μ§ λͺ»νλ€.