# PANDUAN LENGKAP TRIGGER DAN PERTANYAAN EVALUASI DOSEN

## 📚 BAGIAN 1: PENJELASAN LENGKAP TRIGGER

### Apa itu Trigger?

**Trigger** adalah objek database yang **otomatis dijalankan** (triggered/dipicu) saat terjadi event tertentu pada tabel (INSERT, UPDATE, atau DELETE).

### Karakteristik Trigger:
- ✅ **Otomatis** - Tidak perlu dipanggil manual
- ✅ **Event-Driven** - Dijalankan saat INSERT/UPDATE/DELETE
- ✅ **Transparent** - User tidak tahu ada trigger yang berjalan
- ✅ **Atomic** - Bagian dari transaksi yang sama

---

## 🔧 STRUKTUR DASAR TRIGGER

```sql
DELIMITER $$

CREATE TRIGGER nama_trigger
{BEFORE | AFTER} {INSERT | UPDATE | DELETE}
ON nama_tabel
FOR EACH ROW
BEGIN
    -- Logika trigger di sini
    -- Gunakan OLD.column untuk nilai lama (UPDATE/DELETE)
    -- Gunakan NEW.column untuk nilai baru (INSERT/UPDATE)
END$$

DELIMITER ;
```

### Komponen Penting:

1. **DELIMITER**: Ubah delimiter agar bisa pakai `;` di dalam trigger
2. **BEFORE/AFTER**: Kapan trigger dijalankan
3. **INSERT/UPDATE/DELETE**: Event apa yang memicu
4. **FOR EACH ROW**: Trigger dijalankan untuk setiap baris
5. **OLD/NEW**: Akses nilai sebelum dan sesudah perubahan

---

## 📖 CONTOH TRIGGER DARI DATABASE PERPUSTAKAAN

### 1️⃣ TRIGGER VALIDASI (BEFORE INSERT)

**Use Case**: Cegah peminjaman jika user sudah pinjam 2 buku

```sql
DELIMITER $$

CREATE TRIGGER `trg_before_peminjaman_insert_check_max_borrow` 
BEFORE INSERT ON `peminjaman` 
FOR EACH ROW 
BEGIN
    DECLARE current_loans INT;
    DECLARE max_loans INT DEFAULT 2;
    
    -- Hitung peminjaman aktif user ini
    SELECT COUNT(*) INTO current_loans
    FROM peminjaman
    WHERE id_user = NEW.id_user
      AND status_peminjaman = 'Dipinjam';
    
    -- Block jika sudah maksimal
    IF current_loans >= max_loans THEN
        SIGNAL SQLSTATE '45000'
        SET MESSAGE_TEXT = 'User sudah mencapai batas maksimal peminjaman!';
    END IF;
END$$

DELIMITER ;
```

**Penjelasan**:
- `BEFORE INSERT` - Validasi SEBELUM data masuk
- `NEW.id_user` - Ambil id_user dari data yang akan diinsert
- `SIGNAL SQLSTATE '45000'` - Raise error, batalkan INSERT
- Trigger ini OTOMATIS jalan setiap ada INSERT ke tabel peminjaman

**Kenapa pakai BEFORE?**
- Untuk VALIDASI, pakai **BEFORE** karena bisa BATALKAN transaksi
- User langsung dapat error message

---

### 2️⃣ TRIGGER AUTO-GENERATE (BEFORE INSERT)

**Use Case**: Auto-generate nomor inventaris buku

```sql
DELIMITER $$

CREATE TRIGGER `trg_before_aset_buku_insert_generate_number` 
BEFORE INSERT ON `aset_buku` 
FOR EACH ROW 
BEGIN
    DECLARE next_num INT;
    
    -- Generate hanya jika belum ada nomor
    IF NEW.nomor_inventaris IS NULL OR NEW.nomor_inventaris = '' THEN
        -- Hitung nomor urut
        SELECT COUNT(*) + 1 INTO next_num
        FROM aset_buku
        WHERE id_buku = NEW.id_buku;
        
        -- Format: BK-003-001
        SET NEW.nomor_inventaris = CONCAT(
            'BK-',
            LPAD(NEW.id_buku, 3, '0'),
            '-',
            LPAD(next_num, 3, '0')
        );
    END IF;
END$$

DELIMITER ;
```

**Penjelasan**:
- `SET NEW.kolom` - Ubah nilai SEBELUM disimpan
- `LPAD()` - Padding angka jadi 3 digit (003, 055)
- Hasil: BK-003-001, BK-003-002, dst.

**Kenapa pakai BEFORE?**
- Bisa UBAH nilai NEW sebelum disimpan ke database

---

### 3️⃣ TRIGGER LOGGING (AFTER UPDATE)

**Use Case**: Log perubahan data buku

```sql
DELIMITER $$

CREATE TRIGGER `trg_after_buku_update_log` 
AFTER UPDATE ON `buku` 
FOR EACH ROW 
BEGIN
    DECLARE changes TEXT DEFAULT '';
    
    -- Track perubahan judul
    IF OLD.judul != NEW.judul THEN
        SET changes = CONCAT(changes, 'Judul: ', OLD.judul, ' → ', NEW.judul, '; ');
    END IF;
    
    -- Track perubahan stok
    IF OLD.stok_tersedia != NEW.stok_tersedia THEN
        SET changes = CONCAT(changes, 'Stok: ', OLD.stok_tersedia, ' → ', NEW.stok_tersedia, '; ');
    END IF;
    
    -- Log jika ada perubahan
    IF changes != '' THEN
        INSERT INTO log_aktivitas (
            id_user, username, nama_tabel, operasi, deskripsi, id_terkait
        ) VALUES (
            0, 'SYSTEM', 'buku', 'update',
            CONCAT('Buku #', NEW.id_buku, ' updated: ', changes),
            NEW.id_buku
        );
    END IF;
END$$

DELIMITER ;
```

**Penjelasan**:
- `AFTER UPDATE` - Jalan SETELAH data berhasil diupdate
- `OLD.kolom` - Nilai SEBELUM update
- `NEW.kolom` - Nilai SETELAH update
- Insert ke tabel log_aktivitas untuk audit trail

**Kenapa pakai AFTER?**
- Untuk LOGGING, pakai **AFTER** karena hanya catat SETELAH berhasil
- Tidak perlu batalkan transaksi

---

### 4️⃣ TRIGGER AUTO-UPDATE STOK (AFTER INSERT & AFTER UPDATE)

**Use Case**: Update stok buku saat pinjam dan kembali

**Trigger 1: Kurangi stok saat pinjam**
```sql
DELIMITER $$

CREATE TRIGGER `trg_after_peminjaman_insert_decrease_stock` 
AFTER INSERT ON `peminjaman` 
FOR EACH ROW 
BEGIN
    UPDATE buku b
    INNER JOIN aset_buku ab ON b.id_buku = ab.id_buku
    SET b.stok_tersedia = b.stok_tersedia - 1
    WHERE ab.id_aset = NEW.id_aset_buku
      AND b.stok_tersedia > 0;
END$$

DELIMITER ;
```

**Trigger 2: Tambah stok saat kembali**
```sql
DELIMITER $$

CREATE TRIGGER `trg_after_peminjaman_update_increase_stock` 
AFTER UPDATE ON `peminjaman` 
FOR EACH ROW 
BEGIN
    -- Cek jika status berubah dari Dipinjam ke Dikembalikan/Terlambat
    IF OLD.status_peminjaman = 'Dipinjam' 
       AND NEW.status_peminjaman IN ('Dikembalikan', 'Terlambat')
       AND NEW.tanggal_kembali IS NOT NULL THEN
        
        UPDATE buku b
        INNER JOIN aset_buku ab ON b.id_buku = ab.id_buku
        SET b.stok_tersedia = b.stok_tersedia + 1
        WHERE ab.id_aset = NEW.id_aset_buku;
    END IF;
END$$

DELIMITER ;
```

**Penjelasan**:
- Stok otomatis berkurang saat INSERT peminjaman
- Stok otomatis bertambah saat UPDATE status jadi Dikembalikan
- User tidak perlu manual update stok!

---

### 5️⃣ TRIGGER KALKULASI (BEFORE UPDATE)

**Use Case**: Hitung denda otomatis saat pengembalian

```sql
DELIMITER $$

CREATE TRIGGER `trg_before_peminjaman_update_calculate_fine` 
BEFORE UPDATE ON `peminjaman` 
FOR EACH ROW 
BEGIN
    DECLARE denda_per_hari INT DEFAULT 500;
    DECLARE hari_terlambat INT;
    
    -- Ambil tarif denda dari aturan
    SELECT CAST(isi_aturan AS UNSIGNED) INTO denda_per_hari
    FROM aturan_perpustakaan
    WHERE nama_aturan = 'denda_per_hari'
    LIMIT 1;
    
    -- Jika buku baru dikembalikan
    IF NEW.tanggal_kembali IS NOT NULL 
       AND OLD.tanggal_kembali IS NULL THEN
        
        -- Hitung hari terlambat
        SET hari_terlambat = DATEDIFF(NEW.tanggal_kembali, NEW.tanggal_jatuh_tempo);
        
        -- Jika terlambat, hitung denda
        IF hari_terlambat > 0 THEN
            SET NEW.denda = hari_terlambat * denda_per_hari;
            SET NEW.status_peminjaman = 'Terlambat';
        ELSE
            SET NEW.denda = 0;
            SET NEW.status_peminjaman = 'Dikembalikan';
        END IF;
    END IF;
END$$

DELIMITER ;
```

**Penjelasan**:
- Otomatis hitung denda saat tanggal_kembali diisi
- `DATEDIFF()` - Hitung selisih hari
- Set status otomatis: Terlambat atau Dikembalikan

---

## 🎯 KAPAN PAKAI BEFORE vs AFTER?

| Kegunaan | BEFORE | AFTER |
|----------|--------|-------|
| **Validasi** (block jika invalid) | ✅ Ya | ❌ Tidak |
| **Ubah nilai** (SET NEW.kolom) | ✅ Ya | ❌ Tidak |
| **Auto-generate** ID/nomor | ✅ Ya | ❌ Tidak |
| **Logging/Audit** | ❌ Tidak | ✅ Ya |
| **Update tabel lain** | ❌ Tidak* | ✅ Ya |
| **Kalkulasi otomatis** | ✅ Ya | ⚠️ Bisa |

*Bisa, tapi tidak recommended

---

## 📊 PERBANDINGAN: TRIGGER vs STORED PROCEDURE vs FUNCTION

| Fitur | Trigger | Stored Procedure | Function |
|-------|---------|------------------|----------|
| **Dipanggil** | Otomatis | Manual (CALL) | Manual (SELECT) |
| **Event-Driven** | ✅ Ya | ❌ Tidak | ❌ Tidak |
| **Return Value** | ❌ Tidak | ✅ Ya (OUT param) | ✅ Ya (RETURN) |
| **Transaction** | Part of caller | Own transaction | Part of caller |
| **Use Case** | Validasi, Auto-update | Complex business logic | Kalkulasi, Reusable logic |

---

## 💡 BEST PRACTICES TRIGGER

### ✅ DO:
1. **Keep it simple** - Trigger harus cepat, jangan kompleks
2. **Single responsibility** - 1 trigger untuk 1 tujuan
3. **Use BEFORE for validation** - Validasi sebelum data masuk
4. **Use AFTER for logging** - Log setelah transaksi berhasil
5. **Document well** - Beri komentar yang jelas

### ❌ DON'T:
1. **Jangan trigger chain** - Trigger yang memanggil trigger lain (infinite loop!)
2. **Jangan query berat** - Trigger harus cepat
3. **Jangan ubah tabel yang sama** - Bisa infinite loop
4. **Jangan terlalu banyak logic** - Pindahkan ke stored procedure
5. **Jangan block transaksi lama** - Performance issue

---

## 🔥 CONTOH KESALAHAN UMUM

### ❌ KESALAHAN 1: Infinite Loop
```sql
-- JANGAN LAKUKAN INI!
CREATE TRIGGER bad_trigger
AFTER UPDATE ON peminjaman
FOR EACH ROW
BEGIN
    UPDATE peminjaman SET denda = 1000 WHERE id_peminjaman = NEW.id_peminjaman;
    -- Ini akan trigger lagi, infinite loop!
END;
```

### ✅ SOLUSI:
```sql
-- Pakai BEFORE dan SET NEW
CREATE TRIGGER good_trigger
BEFORE UPDATE ON peminjaman
FOR EACH ROW
BEGIN
    SET NEW.denda = 1000; -- Tidak trigger lagi
END;
```

---

### ❌ KESALAHAN 2: Tidak Cek Kondisi
```sql
-- Trigger jalan untuk SEMUA update, meskipun tidak perlu
CREATE TRIGGER bad_trigger
AFTER UPDATE ON peminjaman
FOR EACH ROW
BEGIN
    -- Ini jalan meskipun cuma update catatan!
    UPDATE buku SET stok_tersedia = stok_tersedia + 1;
END;
```

### ✅ SOLUSI:
```sql
-- SELALU CEK KONDISI
CREATE TRIGGER good_trigger
AFTER UPDATE ON peminjaman
FOR EACH ROW
BEGIN
    -- Hanya jalan jika status berubah
    IF OLD.status_peminjaman != NEW.status_peminjaman THEN
        -- Logic here
    END IF;
END;
```

---

## 🎓 BAGIAN 2: PERTANYAAN EVALUASI DARI PERSPEKTIF DOSEN

### 📝 KATEGORI A: KONSEP DASAR (Bobot 20%)

#### 1. **Jelaskan perbedaan antara Trigger, Stored Procedure, dan Function!**

**Jawaban yang Diharapkan**:
- **Trigger**: Otomatis dijalankan saat event (INSERT/UPDATE/DELETE), tidak bisa dipanggil manual
- **Stored Procedure**: Harus dipanggil manual dengan CALL, bisa punya parameter IN/OUT
- **Function**: Dipanggil dalam query SELECT, HARUS return value, tidak boleh modify data (best practice)

**Poin Plus**:
- Sebutkan kapan pakai masing-masing
- Berikan contoh konkret dari project

---

#### 2. **Kapan menggunakan BEFORE trigger dan kapan AFTER trigger?**

**Jawaban yang Diharapkan**:
- **BEFORE**: Untuk validasi (bisa batalkan transaksi) dan auto-generate (bisa ubah NEW)
- **AFTER**: Untuk logging dan update tabel lain (data sudah pasti tersimpan)

**Poin Plus**:
- Contoh konkret dari database perpustakaan
- Jelaskan konsekuensi jika salah pilih

---

#### 3. **Apa itu OLD dan NEW dalam trigger? Kapan bisa dipakai?**

| Event | OLD | NEW |
|-------|-----|-----|
| INSERT | ❌ Tidak ada | ✅ Data baru |
| UPDATE | ✅ Data lama | ✅ Data baru |
| DELETE | ✅ Data yang dihapus | ❌ Tidak ada |

**Poin Plus**:
- Jelaskan bahwa OLD = read-only, NEW = bisa diubah (di BEFORE)

---

### 📝 KATEGORI B: IMPLEMENTASI (Bobot 40%)

#### 4. **Jelaskan trigger untuk membatasi peminjaman maksimal 2 buku!**

**Pertanyaan Lanjutan**:
- Kenapa pakai BEFORE INSERT, bukan AFTER?
- Bagaimana cara trigger ini "tahu" user sudah pinjam berapa buku?
- Apa yang terjadi jika trigger ini dihapus?
- Bagaimana jika ingin ubah batas jadi 3 buku?

**Jawaban yang Diharapkan**:
```sql
- Pakai BEFORE INSERT karena perlu VALIDASI sebelum data masuk
- Query COUNT(*) ke tabel peminjaman untuk hitung pinjaman aktif
- Pakai SIGNAL SQLSTATE untuk raise error dan batalkan INSERT
- Jika trigger dihapus, tidak ada validasi otomatis, tergantung aplikasi
- Ubah DECLARE max_loans INT DEFAULT 2 jadi 3
```

**Poin Plus**:
- Sebutkan bisa ambil dari tabel `aturan_perpustakaan` agar dinamis
- Jelaskan trade-off: hardcode vs dynamic

---

#### 5. **Bagaimana trigger menghitung denda keterlambatan secara otomatis?**

**Pertanyaan Lanjutan**:
- Kenapa pakai BEFORE UPDATE, bukan AFTER?
- Bagaimana cara trigger tahu ini pengembalian pertama kali?
- Apa fungsi DATEDIFF()?
- Bagaimana jika buku dikembalikan lebih awal dari jatuh tempo?

**Jawaban yang Diharapkan**:
```sql
- BEFORE UPDATE karena perlu SET NEW.denda sebelum disimpan
- Cek: NEW.tanggal_kembali IS NOT NULL AND OLD.tanggal_kembali IS NULL
- DATEDIFF() hitung selisih hari antara tanggal kembali dan jatuh tempo
- Jika lebih awal (negatif), set denda = 0
```

---

#### 6. **Jelaskan trigger untuk auto-generate nomor inventaris!**

**Pertanyaan Lanjutan**:
- Format nomor inventaris seperti apa?
- Kenapa pakai LPAD()?
- Bagaimana cara trigger tahu nomor urut berikutnya?
- Apa yang terjadi jika ada 2 INSERT bersamaan? (Race condition)

**Jawaban yang Diharapkan**:
```sql
- Format: BK-003-001 (BK-[id_buku 3 digit]-[urutan 3 digit])
- LPAD() untuk padding 0 di depan (3 jadi 003)
- Query COUNT(*) + 1 dari aset_buku dengan id_buku yang sama
- Race condition bisa terjadi, solusi: pakai AUTO_INCREMENT atau LOCK
```

**Poin Plus**:
- Alternatif: `SELECT COALESCE(MAX(urutan), 0) + 1`
- Sebutkan potensi masalah concurrency

---

### 📝 KATEGORI C: BUSINESS LOGIC (Bobot 25%)

#### 7. **Kenapa perlu trigger untuk validasi denda belum dibayar sebelum pinjam?**

**Pertanyaan Mendalam**:
- Apa dampak bisnis jika tidak ada validasi ini?
- Apakah validasi ini bisa dilakukan di aplikasi saja?
- Bagaimana trigger ini bekerja dengan tabel `pembayaran_denda` yang support cicilan?

**Jawaban yang Diharapkan**:
- Mencegah penumpukan denda, user harus bayar dulu
- Validasi di database lebih aman, tidak bisa di-bypass
- Trigger hitung total denda, bukan cek pembayaran (itu beda validasi)

---

#### 8. **Jelaskan alur lengkap dari peminjaman hingga pengembalian, trigger mana saja yang jalan?**

**Jawaban yang Diharapkan**:
1. **Request Peminjaman**: User insert ke `request_peminjaman`
   - ✅ `trg_before_request_peminjaman_update_set_date` (saat diproses)

2. **Approval**: Petugas approve, insert ke `peminjaman`
   - ✅ `trg_before_peminjaman_insert_check_account_status`
   - ✅ `trg_before_peminjaman_insert_check_max_borrow`
   - ✅ `trg_before_peminjaman_insert_check_unpaid_fine`
   - ✅ `trg_after_peminjaman_insert_decrease_stock`

3. **Jatuh Tempo Terlewat**: Cron job jalankan `sp_update_status_terlambat()`
   - Status jadi "Terlambat", denda mulai dihitung

4. **Pengembalian**: Update `tanggal_kembali`
   - ✅ `trg_before_peminjaman_update_calculate_fine`
   - ✅ `trg_after_peminjaman_update_increase_stock`

**Poin Plus**:
- Buat diagram alur
- Sebutkan urutan eksekusi trigger

---

#### 9. **Apa yang terjadi jika trigger untuk update stok dihapus?**

**Jawaban yang Diharapkan**:
- Stok tidak otomatis berkurang/bertambah
- Harus manual UPDATE tabel buku setiap kali pinjam/kembali
- Data stok bisa tidak akurat
- Katalog bisa menampilkan stok salah, user bingung

**Poin Plus**:
- Sebutkan ini adalah **data integrity issue**
- Trigger memastikan **consistency** antara tabel peminjaman dan buku

---

### 📝 KATEGORI D: ADVANCED (Bobot 15%)

#### 10. **Bagaimana cara debugging trigger yang error?**

**Jawaban yang Diharapkan**:
- Cek `SHOW TRIGGERS FROM database_name;`
- Lihat error message di MySQL error log
- Test dengan `INSERT/UPDATE` manual dan lihat pesan error
- Temporary disable trigger: `DROP TRIGGER nama_trigger;`
- Add logging: INSERT ke tabel log di dalam trigger

**Poin Plus**:
- Gunakan `SELECT` test di MySQL Workbench/phpMyAdmin dulu
- Pakai `SHOW CREATE TRIGGER nama_trigger;` untuk lihat definisi

---

#### 11. **Apa risiko jika terlalu banyak trigger di satu tabel?**

**Jawaban yang Diharapkan**:
- **Performance**: Setiap INSERT/UPDATE jadi lambat
- **Complexity**: Susah tracking flow data
- **Debugging**: Sulit cari bug karena logic tersebar
- **Maintenance**: Sulit ubah business logic

**Solusi**:
- Combine beberapa trigger jadi satu (jika masuk akal)
- Pindahkan logic kompleks ke stored procedure
- Dokumentasi yang jelas

---

#### 12. **Bagaimana trigger berinteraksi dengan transaction? Apa yang terjadi jika trigger error?**

**Jawaban yang Diharapkan**:
- Trigger adalah **bagian dari transaksi** yang sama
- Jika trigger error, **seluruh transaksi di-rollback**
- Termasuk INSERT/UPDATE yang memicu trigger

**Contoh**:
```sql
START TRANSACTION;
INSERT INTO peminjaman (...); -- Trigger jalan
-- Jika trigger raise SIGNAL SQLSTATE, INSERT di-rollback
COMMIT;
```

**Poin Plus**:
- Sebutkan konsep **ACID** (Atomicity)
- Jelaskan ini bagus untuk data integrity

---

### 📝 KATEGORI E: DESIGN DECISION (Bobot: Insight)

#### 13. **Kenapa trigger untuk cek stok tidak ada? Kenapa hanya bergantung pada validasi aplikasi?**

**Ini Pertanyaan Jebakan!**

**Jawaban yang Baik**:
- "Sebenarnya SEBAIKNYA ada trigger untuk cek stok > 0"
- "Saat ini bergantung pada aplikasi, ini WEAKNESS"
- "Saya bisa menambahkan trigger seperti ini:"

```sql
CREATE TRIGGER trg_before_peminjaman_insert_check_stock
BEFORE INSERT ON peminjaman
FOR EACH ROW
BEGIN
    DECLARE stok INT;
    
    SELECT b.stok_tersedia INTO stok
    FROM buku b
    INNER JOIN aset_buku ab ON b.id_buku = ab.id_buku
    WHERE ab.id_aset = NEW.id_aset_buku;
    
    IF stok <= 0 THEN
        SIGNAL SQLSTATE '45000'
        SET MESSAGE_TEXT = 'Stok buku habis!';
    END IF;
END;
```

**Poin Plus**:
- Tunjukkan critical thinking
- Identifikasi area improvement

---

#### 14. **Kenapa ada function `fn_boleh_pinjam()` DAN trigger `trg_before_peminjaman_insert_check_max_borrow`? Bukankah redundant?**

**Jawaban yang Baik**:
- **Function**: Untuk CEK di UI (show badge "BOLEH/TIDAK BOLEH")
- **Trigger**: Untuk ENFORCE di database level (last line of defense)
- Bukan redundant, tapi **defense in depth**

**Analogi**:
- Function = "Pagar" (warning)
- Trigger = "Pintu terkunci" (prevention)

User bisa bypass UI, tapi tidak bisa bypass trigger!

---

#### 15. **Bagaimana Anda memutuskan logic mana yang pakai Trigger, Stored Procedure, atau Function?**

**Decision Matrix**:

| Kriteria | Trigger | Stored Procedure | Function |
|----------|---------|------------------|----------|
| **Perlu otomatis?** | ✅ Ya | ❌ Manual | ❌ Manual |
| **Perlu validasi hard?** | ✅ Ya | ⚠️ Bisa | ❌ Read-only |
| **Complex query?** | ❌ Tidak | ✅ Ya | ⚠️ Simple |
| **Reusable di SELECT?** | ❌ Tidak | ❌ Tidak | ✅ Ya |
| **Modify multiple tables?** | ⚠️ Bisa | ✅ Ya | ❌ Tidak |

**Poin Plus**:
- Berikan contoh konkret dari project
- Jelaskan trade-off masing-masing

---

## 🎯 TIPS MENJAWAB SAAT PRESENTASI

### 1. **SELALU AWALI DENGAN DEFINISI**
"Trigger adalah objek database yang otomatis dijalankan saat terjadi event INSERT, UPDATE, atau DELETE pada tabel tertentu"

### 2. **BERIKAN CONTOH KONKRET**
Jangan cuma teori, tunjukkan code dari project Anda!

### 3. **JELASKAN "KENAPA", BUKAN HANYA "APA"**
- ❌ "Ini trigger untuk validasi"
- ✅ "Trigger ini mencegah user meminjam lebih dari 2 buku karena akan overload perpustakaan dan merugikan user lain"

### 4. **AKUI KETERBATASAN**
"Trigger ini bekerja dengan baik untuk concurrency rendah, tapi untuk high-traffic perlu optimization seperti..."

### 5. **TUNJUKKAN ALTERNATIF**
"Selain trigger, kita juga bisa pakai CHECK CONSTRAINT, tapi trigger lebih flexible karena bisa query tabel lain"

---

## 📊 RUBRIK PENILAIAN DOSEN (Prediksi)

| Aspek | Bobot | Kriteria Excellent |
|-------|-------|-------------------|
| **Konsep Dasar** | 20% | Paham bedanya trigger/procedure/function, kapan pakai |
| **Implementasi** | 40% | Trigger jalan dengan benar, handle edge case |
| **Business Logic** | 25% | Trigger sesuai kebutuhan bisnis perpustakaan |
| **Code Quality** | 10% | Clean, readable, documented |
| **Critical Thinking** | 5% | Identify trade-offs, suggest improvement |

---

## 💪 LATIHAN SOAL

### Soal 1: Buat trigger untuk prevent delete buku yang sedang dipinjam

<details>
<summary>Lihat Solusi</summary>

```sql
DELIMITER $$

CREATE TRIGGER trg_before_buku_delete_check_active_loans
BEFORE DELETE ON buku
FOR EACH ROW
BEGIN
    DECLARE active_loans INT;
    
    SELECT COUNT(*) INTO active_loans
    FROM peminjaman p
    INNER JOIN aset_buku ab ON p.id_aset_buku = ab.id_aset
    WHERE ab.id_buku = OLD.id_buku
      AND p.status_peminjaman = 'Dipinjam';
    
    IF active_loans > 0 THEN
        SIGNAL SQLSTATE '45000'
        SET MESSAGE_TEXT = 'Tidak bisa hapus buku yang sedang dipinjam!';
    END IF;
END$$

DELIMITER ;
```
</details>

---

### Soal 2: Buat trigger untuk prevent stok buku negatif

<details>
<summary>Lihat Solusi</summary>

```sql
DELIMITER $$

CREATE TRIGGER trg_before_buku_update_prevent_negative
BEFORE UPDATE ON buku
FOR EACH ROW
BEGIN
    IF NEW.stok_tersedia < 0 THEN
        SIGNAL SQLSTATE '45000'
        SET MESSAGE_TEXT = 'Stok buku tidak boleh negatif!';
    END IF;
END$$

DELIMITER ;
```
</details>

---

### Soal 3: Buat trigger untuk auto set tanggal_diproses saat request approve/reject

<details>
<summary>Lihat Solusi</summary>

```sql
DELIMITER $$

CREATE TRIGGER trg_before_request_peminjaman_update_set_date
BEFORE UPDATE ON request_peminjaman
FOR EACH ROW
BEGIN
    IF OLD.status = 'pending' 
       AND NEW.status IN ('disetujui', ' ditolak')
       AND NEW.tanggal_diproses IS NULL THEN
        SET NEW.tanggal_diproses = NOW();
    END IF;
END$$

DELIMITER ;
```
</details>

---

## 🎓 KESIMPULAN

### Trigger adalah:
- ✅ **Powerful** - Automasi dan enforcement di database level
- ✅ **Reliable** - Tidak bisa di-bypass
- ✅ **Transparent** - User tidak perlu tahu
- ⚠️ **Complex** - Susah debug jika tidak didokumentasikan
- ⚠️ **Performance Impact** - Jika terlalu kompleks

### Best Practice:
1. Keep triggers simple and fast
2. Document everything
3. Test edge cases
4. Use BEFORE for validation, AFTER for logging
5. Avoid trigger chains

### Key Takeaway untuk Presentasi:
**"Trigger memastikan business rules tetap enforce meskipun aplikasi berubah atau ada bug di code. Ini adalah last line of defense untuk data integrity."**

---

**Semoga sukses presentasinya!** 🚀

---

*Dokumentasi ini dibuat untuk membantu persiapan presentasi dan ujian Manajemen Basis Data*
