İçeriğe geç

Soft Delete — Silinen Hesap Gerçekten Gitti mi?

Orta 9 dk Sık karşılaşılır

Önce şunu oku: Toplu UPDATE — Veritabanı Değişti, Nesnen Neden Haberi Yok?

30 saniyede özet

Bankada bir kaydı gerçekten silmek çoğu zaman yasaktır: denetçi yıllar sonra sorabilir. Soft delete satırı silmek yerine işaretler. Ama işaret her sorguda süzülmezse silinen satır geri gelir, benzersiz indeksler de eski satıra takılır.

Bir kütüphane, rafından kaldırdığı kitapları çöpe atmaz; üstüne “kaldırıldı” etiketi yapıştırır. Katalog bilgisayarı etiketli kitapları göstermez. Ama rafa doğrudan bakan biri, o kitabı hâlâ orada görür.

  1. Bayt: Müşteri TR01 hesabını kapattı. Silmek yerine deleted = true yaptık, denetim için kalsın!

  2. Sen: Ama aktif hesaplar listesinde TR01 hâlâ görünüyor. Gece raporu da onu sayıyor.

  3. Bayt: Bir de aynı IBAN'la yeni hesap açılamıyor...

  4. Bayt: Satırı işaretledik ama işareti kimin okuduğuna bakmadık. Bir bakalım.

Neden silmiyoruz?

Bankalar kapanan bir hesabın bilgisini yıllarca saklamak zorundadır; denetçi ya da mahkeme sorabilir. Satırı DELETE ile silmek bu bilgiyi geri dönülmez biçimde yok eder.

soft deleteSatırı silmek yerine deleted gibi bir alanla işaretleyip uygulamada gizlemek. Bilgi denetim için tabloda kalır.Sözlükte gör →, satırı silmek yerine deleted = true gibi bir alanla işaretlemektir. Satır tabloda kalır; uygulama onu “yokmuş gibi” davranarak gizler.

Kafam karıştı, daha basit anlat

Soft delete: satırı silme, işaretle. Bilgi kalır, ekranda görünmez.

Hızlı kontrolBaşlangıç

Soft delete nedir?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Bir bankada kapanan hesabı DELETE ile silmenin sakıncası nedir?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

İşareti kim okur?

Hesap varlığının üstünde @SQLRestriction("deleted = false") var. Gece raporu ise native SQL ile SELECT * FROM account yapıyor. Kapanan hesap raporda görünür mü? Cevabı göster

Görünür. @SQLRestriction yalnızca Hibernate’in kendi yazdığı sorgulara, yani varlık yüklemelerine ve JPQL/HQL sorgularına eklenir. Native SQL’i Hibernate yazmaz; koşulu raporun kendisi eklemelidir.

Etiket yetmez: herkes kataloğa bakmalı.
Adım adım oku
  1. Kitabı atmak: arşiv için de gider.
  2. "Kaldırıldı" etiketi: kitap rafta kalır.
  3. Katalog gizler, rafa doğrudan bakan görür.
  4. Etiket yetmez: herkes kataloğa bakmalı.

İşareti her sorguda elle süzmek, unutulan her sorguda silinen satırı geri getirir. Hibernate’in @SQLRestriction notası filtreyi varlığın üstüne koyar ve Hibernate’in yazdığı her sorguya kendisi ekler. Ama Hibernate’in yazmadığı bir native SQLHibernate'in değil, doğrudan senin yazdığın SQL. Varlığa konan filtreler ve bazı Hibernate özellikleri ona uygulanmaz.Sözlükte gör → sorgusu bu filtreyi görmez.

Bir sorun daha var: silinen satır tabloda kaldığı için IBAN üzerindeki benzersiz indekste yer tutmaya devam eder. Çözüm, yalnızca silinmemiş satırları kapsayan bir kısmi indeksYalnızca bir koşulu sağlayan satırları kapsayan indeks, örneğin WHERE deleted = false. Benzersizliği canlı satırlarla sınırlamak için kullanılır.Sözlükte gör →tir.

Kafam karıştı, daha basit anlat

Filtreyi varlığa koy; native SQL’de kendin yaz. Benzersizliği yalnızca canlı satırlara uygula.

Hızlı kontrolOrta

@SQLRestriction("deleted = false") ne yapar?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Kapanan hesabın IBAN'ıyla yeni hesap açılamıyor. Neden?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Kendin gör

Silinen hesap gerçekten gitti mi?

Tohum 859859

    Oynat ya da adımla: her adımda bir durum yaşanır.

    Hız
    Adım 0

    Şu an ne oldu?

    deleted = true (filtre sorgularda)

    Müşteri TR01 hesabını kapattı. Dört durum yaşanacak.

    Görevler0/3

    • Kapanan hesabı aktif listede gösteraçık

      İpucu

      Varsayılan ayarlar yeter.

    • Aynı IBAN’la yeni hesap açarken çakışaçık

      İpucu

      Silinen satır tabloda kalsın.

    • Hibernate sorgularını koru ama native raporu kaçıraçık

      İpucu

      Filtreyi varlığa koy, indeksi düzelt.

    Olay günlüğü (0)

    Henüz olay yok. Oynat veya adımla.

    1. Varsayılanla oynat. Elle yazılan filtre: kapanan hesap aktif listede göründü, yeni hesap IBAN’a takıldı, gece raporu onu saydı.
    2. “@SQLRestriction” seç. Liste düzeldi; ama IBAN çakışması ve native rapor sorunu sürdü.
    3. “Kısmi benzersiz indeks”i aç. IBAN yeniden kullanılabildi; geriye yalnızca native rapor kaldı.
    4. “DELETE” seç. Her şey temiz görünüyor, ama denetçiye verecek bilgi kalmadı.
    Hızlı kontrolOrta

    Kısmi benzersiz indeks (UNIQUE ... WHERE deleted = false) neyi çözer?

    Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

    Gece raporu native SQL ile SELECT count(*) FROM account yapıyor. Varlıkta @SQLRestriction var. Sonuç ne olur?

    Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

    Tuzaklar

    Native sorguları unutmak: raporlar ve toplu işler çoğu zaman native SQL yazar. Onları deleted = false koşuluyla yaz ya da yalnızca canlı satırları gösteren bir veritabanı görünümünden oku.

    Benzersiz indeksi olduğu gibi bırakmak. Silinen satır aynı IBAN’ı tutmaya devam eder. Benzersizliği WHERE deleted = false ile yalnızca canlı satırlara uygula.

    Kim ve ne zaman sildi bilgisini atlamak. Yalnızca deleted = true demek, denetçinin asıl sorusunu cevaplamaz. Silme zamanını ve kimin sildiğini de sakla.

    Kafam karıştı, daha basit anlat

    Native SQL’de filtreyi yaz, indeksi canlı satırlara daralt, kim ve ne zaman bilgisini sakla.

    Hızlı kontrolOrta

    Yalnızca deleted = true saklamak denetçinin hangi sorusunu cevapsız bırakır?

    Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

    Aşağıdaki örnek bir bankanın hesap tablosundan: Hibernate’in silmeyi güncellemeye çevirdiği, filtreyi kendisi eklediği ve kimin sildiğini sakladığı bir varlık.

    Derinleş · Kapanan hesaplar: soft delete, kısmi indeks, native rapor 4 dosya · ~55 satır · ilk okumada atlayabilirsin
    Proje dosyaları

    src/main/java/com/bank/account/ Account.java Varlık: delete çağrısı bir UPDATE'e çevriliyor; Hibernate'in yazdığı her sorguya deleted = false ekleniyor.

    src/main/java/com/bank/account/Account.java
    @Entity
    @SQLDelete(sql = "UPDATE account SET deleted = true, closed_at = now() WHERE id = ?") // delete becomes an update
    @SQLRestriction("deleted = false") // added to entity loads and JPQL/HQL, not to native SQL
    public class Account {
    @Id
    @GeneratedValue
    private Long id;
    private String iban;
    private boolean deleted;
    private Instant closedAt;
    private String closedBy;
    void markClosedBy(String operator) {
    this.closedBy = operator; // who closed it: the auditor's real question
    }
    }

    src/main/java/com/bank/account/ AccountClosing.java Kapatma: kim ve ne zaman kapattı bilgisi, işaretle birlikte saklanıyor.

    src/main/java/com/bank/account/AccountClosing.java
    @Service
    class AccountClosing {
    private final AccountRepository accounts;
    AccountClosing(AccountRepository accounts) {
    this.accounts = accounts;
    }
    @Transactional
    void close(Long accountId, String operator) {
    Account account = accounts.findById(accountId).orElseThrow();
    account.markClosedBy(operator);
    accounts.delete(account); // runs the @SQLDelete UPDATE; the row stays for the audit trail
    }
    }

    src/main/resources/db/migration/ V33__account_soft_delete.sql İndeks: IBAN yalnızca canlı satırlar arasında benzersiz; kapanan hesabın IBAN'ı yeniden kullanılabilir.

    src/main/resources/db/migration/V33__account_soft_delete.sql
    ALTER TABLE account ADD COLUMN deleted boolean NOT NULL DEFAULT false;
    ALTER TABLE account ADD COLUMN closed_at timestamptz;
    ALTER TABLE account ADD COLUMN closed_by varchar(64);
    -- Unique only among live rows: a closed account's IBAN can be reused.
    DROP INDEX IF EXISTS ux_account_iban;
    CREATE UNIQUE INDEX ux_account_iban_live ON account (iban) WHERE deleted = false;

    src/main/java/com/bank/report/ NightlyBalanceReport.java Rapor: native SQL filtreyi görmez; koşul sorgunun içinde açıkça yazılıyor.

    src/main/java/com/bank/report/NightlyBalanceReport.java
    @Repository
    class NightlyBalanceReport {
    private final JdbcClient jdbc;
    NightlyBalanceReport(JdbcClient jdbc) {
    this.jdbc = jdbc;
    }
    long activeAccounts() {
    // Native SQL: @SQLRestriction does not apply here, so the condition is written out.
    return jdbc.sql("SELECT count(*) FROM account WHERE deleted = false").query(Long.class).single();
    }
    }

    Kendini sına

    Şimşek turu1/4

    Soft delete satırı tablodan siler.

    Soru 1/3İleri

    Filtre her repository sorgusuna elle eklenmiş ve biri yeni bir sorgu yazarken unutmuş. Ne olur?

    Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

    Aklında kalacak üç şey

    1. 1 Soft delete satırı silmez, deleted gibi bir alanla işaretler; böylece kapanan hesabın bilgisi denetim için kalır.
    2. 2 İşaret her sorguda elle süzülürse unutulan her sorgu silinen satırı geri getirir. @SQLRestriction filtreyi varlığın üstüne koyar ve Hibernate'in yazdığı sorgulara kendisi ekler.
    3. 3 @SQLRestriction native SQL'e uygulanmaz ve silinen satır benzersiz indekste yer tutmaya devam eder. Native sorgular filtreyi kendisi yazmalı; benzersizlik kısmi bir indeksle kurulmalı.
    Sonraki kapı Müşteri açıklama kutusuna tuhaf bir metin yazdı ve bütün bankanın hareketlerini gördü. Kutuya ne yazmış olabilir? Dinamik Sorgu — Filtre Ekranı Bir Kapıya Dönüşmesin · 9 dk

    4 kart sonraki derste seni bekliyor

    0/4 kart bu dersten toplandı