Menu Bar

Kata Mutiara

"Keberhasilan merupakan tetesan dari jeri-payah perjuangan, luka, pengorbanan dan hal-hal yang mengejutkan. Kegagalan merupakan tetesan dari kemalasan, kebekuan, kelemahan, kehinaan dan kerendahan"

ANIMASI TULISAN BERJALAN

Showing posts with label Bahasa java. Show all posts
Showing posts with label Bahasa java. Show all posts

Thursday, August 21, 2025

Jenis Join Table & Implementasi

 Topik ini sangat penting untuk sistem perbankan, implementasi join table adalah tulang punggung relasi antar entitas seperti nasabah, rekening, transaksi, dan otorisasi. Mari kita bedah berbagai jenis join dan implementasinya di masing-masing teknologi persistence:


🔗 Jenis Join Table & Implementasi

1. JPA (Java Persistence API)

JPA mendukung join melalui relasi entity dan JPQL:

  • OneToOne / ManyToOne / OneToMany / ManyToMany
  • Gunakan @JoinColumn atau @JoinTable untuk relasi eksplisit
  • Query: SELECT a FROM Account a JOIN a.customer c WHERE c.name = :name

📌 Contoh di banking:

@Entity
public class Account {
  @ManyToOne
  @JoinColumn(name = "customer_id")
  private Customer customer;
}

Digunakan untuk menghubungkan akun dengan nasabah.


2. Hibernate

Hibernate adalah implementasi JPA + fitur tambahan:

  • Mendukung fetching strategies (LAZY, EAGER)
  • Bisa pakai HQL: FROM Account a JOIN a.transactions t WHERE t.amount > 10000
  • Mendukung Named Entity Graphs dan Criteria API

📌 Contoh di banking:
Mengambil transaksi besar dari akun tertentu dengan caching dan lifecycle management.


3. JDBC

JDBC adalah low-level, jadi join dilakukan manual via SQL:

String sql = "SELECT a.*, c.* FROM account a JOIN customer c ON a.customer_id = c.id";
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery();

📌 Contoh di banking:
Digunakan untuk laporan audit atau migrasi data, karena kontrol penuh atas query dan performa.


4. MyBatis

MyBatis mengandalkan XML/Annotation untuk mapping SQL:

<select id="getAccountsWithCustomer" resultMap="accountCustomerMap">
  SELECT a.*, c.* FROM account a JOIN customer c ON a.customer_id = c.id
</select>
  • Bisa pakai resultMap untuk nested mapping
  • Cocok untuk query kompleks dan fleksibel

📌 Contoh di banking:
Digunakan untuk dashboard transaksi, laporan bulanan, atau integrasi dengan sistem legacy.


🏦 Studi Kasus Implementasi di Banking

EntitasRelasi JoinTujuan
Customer ↔ Account@ManyToOne / SQL JOINMenampilkan semua akun milik nasabah
Account ↔ Transaction@OneToMany / JOIN TABLEMelacak histori transaksi dan saldo
Account ↔ Approval@OneToOne / JOIN TABLEMenyimpan status persetujuan kredit atau limit
User ↔ Role ↔ Permission@ManyToManyManajemen otorisasi dan akses sistem

perbedaan antara JDBC, Hibernate, JPA, dan MyBatis

Bedah perbedaan antara JDBC, Hibernate, JPA, dan MyBatis dari sudut pandang arsitektur, kemudahan penggunaan, dan fleksibilitas.


⚙️ Ringkasan Singkat

Teknologi Tipe Abstraksi Mapping Query Style Cocok Untuk
JDBC Low-level API Manual Tidak SQL murni Control penuh, performa
Hibernate ORM Framework Tinggi Ya HQL + Criteria Entity lifecycle, caching
JPA Spesifikasi Tinggi Ya JPQL Abstraksi Hibernate, EclipseLink
MyBatis SQL Mapper Sedang Parsial SQL XML/Annotation Query kompleks, fleksibel

1. JDBC (Java Database Connectivity)

API dasar untuk koneksi dan eksekusi query ke database.

Kelebihan:

  • Kontrol penuh atas query dan transaksi.
  • Performa optimal karena tanpa overhead.

Kekurangan:

  • Banyak boilerplate code (ResultSet, PreparedStatement, dsb).
  • Tidak ada mapping otomatis ke objek Java.
  • Rentan terhadap SQL injection jika tidak hati-hati.

Contoh Penggunaan:

PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE id = ?");
ps.setInt(1, userId);
ResultSet rs = ps.executeQuery();

2. Hibernate

ORM (Object Relational Mapping) yang mengimplementasikan JPA dan menambahkan fitur tambahan.

Kelebihan:

  • Mapping otomatis antara tabel dan objek Java.
  • Caching, lazy loading, dan entity lifecycle management.
  • Mendukung HQL dan Criteria API.

Kekurangan:

  • Learning curve tinggi.
  • Kadang terlalu banyak magic (misalnya auto-fetch yang tidak diinginkan).
  • Debugging bisa rumit.

Contoh Penggunaan:

Session session = sessionFactory.openSession();
User user = session.get(User.class, userId);

3. JPA (Java Persistence API)

Spesifikasi standar untuk ORM di Java. Hibernate adalah salah satu implementasinya.

Kelebihan:

  • Abstraksi tinggi dan standar industri.
  • Integrasi mudah dengan Spring Data JPA.
  • Cocok untuk aplikasi enterprise.

Kekurangan:

  • Terbatas pada fitur yang didefinisikan oleh spesifikasi.
  • Untuk fitur lanjutan, tetap tergantung pada implementasi (misalnya Hibernate).

Contoh Penggunaan (Spring Data JPA):

public interface UserRepository extends JpaRepository<User, Long> {
    List<User> findByStatus(String status);
}

4. MyBatis

Framework SQL Mapper yang menggabungkan fleksibilitas SQL dengan mapping ke objek Java.

Kelebihan:

  • Kontrol penuh atas SQL.
  • Cocok untuk query kompleks dan performa tinggi.
  • Mudah diintegrasikan dengan Spring Boot.

Kekurangan:

  • Tidak ada entity lifecycle management.
  • Mapping manual (walau bisa otomatis sebagian).
  • Tidak cocok untuk aplikasi yang heavily domain-driven.

Contoh Penggunaan:

<select id="selectUser" resultType="User">
  SELECT * FROM users WHERE id = #{id}
</select>

🚀 Kapan Menggunakan yang Mana?

  • JDBC → Saat kamu butuh kontrol penuh dan performa maksimal, misalnya untuk batch processing atau low-level DB ops.
  • Hibernate/JPA → Cocok untuk aplikasi enterprise dengan domain model kompleks dan kebutuhan ORM penuh.
  • MyBatis → Ideal untuk aplikasi yang butuh query kompleks, performa tinggi, dan fleksibilitas SQL.

Jenis Arsitektur Aplikasi Berbasis Java

 🧱 Jenis Arsitektur Aplikasi Berbasis Java

1. Monolithic Architecture

Semua komponen (UI, business logic, data access) digabung dalam satu aplikasi besar.

Kelebihan:

  • Mudah dikembangkan dan dideploy di tahap awal.
  • Debugging dan testing lebih sederhana.
  • Cocok untuk MVP atau aplikasi kecil.

Kekurangan:

  • Sulit diskalakan secara independen.
  • Perubahan kecil bisa memicu redeploy seluruh aplikasi.
  • Rentan terhadap tight coupling antar modul.

2. Layered Architecture (3-tier atau N-tier)

Terbagi menjadi beberapa lapisan: Presentation → Business → Persistence.

Kelebihan:

  • Struktur jelas dan mudah dipahami.
  • Cocok untuk aplikasi enterprise tradisional.
  • Memudahkan pemisahan tanggung jawab.

Kekurangan:

  • Bisa menjadi rigid dan verbose.
  • Tidak cocok untuk kebutuhan real-time atau high-concurrency.

3. Hexagonal Architecture (Ports and Adapters)

Inti aplikasi (domain logic) dikelilingi oleh adapter untuk komunikasi eksternal (REST, DB, Kafka).

Kelebihan:

  • Tingkat isolasi tinggi antara domain dan teknologi.
  • Mudah diuji dan diubah tanpa mengganggu core logic.
  • Cocok untuk DDD dan test-driven development.

Kekurangan:

  • Kurva belajar lebih tinggi.
  • Bisa terasa over-engineered untuk aplikasi kecil.

4. Microservices Architecture

Aplikasi dipecah menjadi layanan-layanan kecil yang independen, masing-masing punya database dan deployment sendiri.

Kelebihan:

  • Skalabilitas dan fleksibilitas tinggi.
  • Tim bisa bekerja paralel pada service berbeda.
  • Fault isolation lebih baik.

Kekurangan:

  • Kompleksitas tinggi (networking, observability, data consistency).
  • Butuh DevOps dan CI/CD yang matang.
  • Latency antar service bisa menjadi bottleneck.

5. Reactive Architecture

Menggunakan event-driven model (misalnya dengan Kafka, WebSocket, Reactor) untuk menangani data stream dan async processing.

Kelebihan:

  • Sangat cocok untuk aplikasi real-time dan high-throughput.
  • Resource usage lebih efisien.
  • Skalabilitas horizontal lebih mudah.

Kekurangan:

  • Debugging dan tracing lebih sulit.
  • Paradigma pemrograman berbeda (non-blocking, backpressure).

🔍 Perbandingan Singkat

Arsitektur Skalabilitas Kompleksitas Cocok untuk Deployment
Monolithic Rendah Rendah MVP, aplikasi kecil Satu unit
Layered Sedang Sedang Aplikasi enterprise Satu unit
Hexagonal Tinggi Sedang-Tinggi DDD, testable apps Modular
Microservices Sangat tinggi Tinggi Sistem besar Per service
Reactive Tinggi Tinggi Real-time, stream Modular / async


Wednesday, February 26, 2025

Cloudflare Waiting Room: Solusi Anti-Crash Saat Trafik Membludak! 🚀

 Cara Kerja Cloudflare Waiting Room

Cloudflare Waiting Room adalah fitur yang digunakan untuk mengelola lonjakan lalu lintas ke situs web dengan mengantri pengunjung saat kapasitas server hampir penuh. Ini berguna untuk mencegah crash akibat lonjakan traffic, seperti saat peluncuran produk atau event besar.


Bagaimana Cloudflare Waiting Room Bekerja?

  1. Pengunjung Mengakses Website

    • Saat pengguna membuka situs web, request mereka akan melewati Cloudflare terlebih dahulu.
  2. Cloudflare Memeriksa Kapasitas Server

    • Jika server masih dalam batas kapasitas yang aman, pengunjung langsung diarahkan ke situs.
    • Jika server sudah hampir penuh, pengunjung dimasukkan ke dalam waiting room.
  3. Waiting Room Menampilkan Estimasi Waktu Tunggu

    • Pengguna yang masuk ke waiting room akan melihat halaman antrian yang berisi perkiraan waktu tunggu.
    • Cloudflare secara otomatis memperbarui status antrian berdasarkan kapasitas yang tersedia.
  4. Akses Diberikan Secara Bertahap

    • Saat ada slot kosong di server, Cloudflare mengeluarkan pengguna dari waiting room secara bergelombang berdasarkan urutan mereka masuk antrian.

Fitur Utama Cloudflare Waiting Room

✅ Auto-Scaling → Menyesuaikan antrian sesuai kapasitas server.
✅ Estimasi Waktu Tunggu → Memberikan pengalaman pengguna yang lebih baik.
✅ Prioritas Akses → Bisa mengatur siapa yang mendapat prioritas lebih dulu.
✅ Konfigurasi Fleksibel → Bisa disesuaikan dengan jumlah pengunjung maksimal dan batas antrian.
✅ Integrasi Cloudflare CDN → Memastikan loading halaman tetap cepat.


Kapan Harus Menggunakan Cloudflare Waiting Room?

🔹 Saat lonjakan trafik tiba-tiba (contoh: flash sale, pendaftaran event).
🔹 Untuk melindungi server dari overload tanpa menambah infrastruktur mahal.
🔹 Jika ingin memberikan pengalaman pengguna yang lebih terkontrol daripada hanya sekadar error 503 (server penuh).

Cloudflare Waiting Room cocok untuk website e-commerce, tiket event, kampanye digital, dan aplikasi yang sering mengalami lonjakan pengguna! 🚀

Mengatasi Express.js Boros CPU & RAM: Penyebab dan Solusi!🚀

 Berikut cara melakukan profiling Express.js menggunakan Node.js Performance Hooks dan clinic.js untuk mengetahui penyebab penggunaan CPU dan RAM yang tinggi.


1. Profiling dengan Node.js Performance Hooks

Node.js menyediakan modul perf_hooks untuk mengukur waktu eksekusi fungsi dan request.

Langkah 1: Tambahkan Performance Hooks di Express.js

Buat middleware untuk mengukur waktu eksekusi setiap request.

const express = require("express");
const { performance, PerformanceObserver } = require("perf_hooks");

const app = express();
const port = 3000;

// Middleware untuk mengukur waktu request
app.use((req, res, next) => {
    const start = performance.now();
    res.on("finish", () => {
        const duration = performance.now() - start;
        console.log(`Request ${req.method} ${req.url} selesai dalam ${duration.toFixed(2)} ms`);
    });
    next();
});

app.get("/", (req, res) => {
    res.send("Hello World!");
});

app.listen(port, () => {
    console.log(`Server berjalan di http://localhost:${port}`);
});

Langkah 2: Jalankan Server dan Lihat Output

node server.js

Kemudian akses beberapa endpoint, misalnya http://localhost:3000/. Console akan menampilkan durasi eksekusi setiap request.


2. Profiling dengan Clinic.js

clinic.js adalah alat yang lebih kuat untuk menganalisis CPU, RAM, dan event loop.

Langkah 1: Install clinic.js

npm install -g clinic

Langkah 2: Jalankan Profiling dengan Clinic.js

clinic doctor -- node server.js

Lalu akses beberapa endpoint dan tekan Ctrl + C untuk menghentikan proses. Clinic.js akan membuat laporan HTML yang bisa dibuka di browser.

Langkah 3: Analisis Hasil

Buka laporan yang dibuat Clinic.js untuk melihat:

  • Memory Leak → jika penggunaan RAM terus naik tanpa turun.
  • Event Loop Blocking → jika ada proses yang menghambat performa.
  • CPU Usage → jika terlalu tinggi karena proses berat.

Kesimpulan

  1. Gunakan Performance Hooks untuk analisis cepat.
  2. Gunakan Clinic.js untuk laporan lebih mendalam.
  3. Optimasi berdasarkan hasil profiling, misalnya:
    • Kurangi query database yang berat.
    • Gunakan caching (Redis).
    • Optimalkan middleware yang tidak perlu.
    • Gunakan PM2 cluster mode untuk load balancing.

Coba jalankan profiling di proyekmu dan kasih tahu hasilnya, biar kita bisa optimasi lebih lanjut! 🚀

Monday, February 17, 2025

Spring Webflux + MongoDB : Panduan CRUD Reactive untuk Pemula

 database mongodb : 

Langkah 1: Membuat database testdb

MongoDB tidak memerlukan perintah eksplisit untuk membuat database. Database akan dibuat secara otomatis saat kamu pertama kali menyimpan data ke dalamnya.

Namun, untuk memastikan bahwa kamu bekerja dengan database testdb, gunakan perintah:

----------------

use testdb

----------------

nama database : testdb

nama tabel : movie

entity :

    int id;
    String plot;
    List<String> genres;
    String title;

example data :

{

  "_id": 1,

  "plot": "A great movie plot",

  "genres": [

    "Action",

    "Drama"

  ],

  "title": "My Movie",

  "_class": "com.joko.springwebflux.model.Movie"

}

-------

{

  "_id": 2,

  "plot": "Junior Youth-hen Episode 8",

  "genres": [

    "Sport",

    "Anime"

  ],

  "title": "Captain Tsubasa Season 2",

  "_class": "com.joko.springwebflux.model.Movie"

}

-------

pom.xml :

<?xml version="1.0" encoding="UTF-8"?>
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.joko</groupId>
    <artifactId>SpringWebFlux</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>SpringWebFlux</name>
    <description>SpringWebFlux</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <image>
                        <builder>paketobuildpacks/builder-jammy-base:latest</builder>
                    </image>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

application.properties : 

server.port=8085

#mongo db config
port=27017
dbname=testdb

#logging.level.org.springframework=DEBUG

aplikasi Spring Boot dengan spring-boot-properties.

server.port=8085 adalah pengaturan untuk menentukan port yang digunakan untuk server aplikasi (dalam hal ini, 8085).

port=27017 adalah pengaturan untuk menentukan port Mongo DB yang digunakan (dalam hal ini, 27017).

dbname=testdb adalah pengaturan untuk menentukan nama database yang digunakan (dalam hal ini, testdb).


model : Movie.java

package com.joko.springwebflux.model;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import java.util.List;

@Document
public class Movie {

    @Id
    int id;
    String plot;
    List<String> genres;
    String title;

    public Movie() {
    }

    public Movie(int id, String plot, List<String> genres, String title) {
        this.id = id;
        this.plot = plot;
        this.genres = genres;
        this.title = title;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getPlot() {
        return plot;
    }

    public void setPlot(String plot) {
        this.plot = plot;
    }

    public List<String> getGenres() {
        return genres;
    }

    public void setGenres(List<String> genres) {
        this.genres = genres;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    @Override
    public String toString() {
        return "Movie{" +
                "id=" + id +
                ", plot='" + plot + '\'' +
                ", genres=" + genres +
                ", title='" + title + '\'' +
                '}';
    }
}


Kode di atas mendefinisikan sebuah model `Movie` yang mewakili entitas "Film" dalam konteks aplikasi berbasis Spring WebFlux dengan MongoDB sebagai penyimpanan datanya.

Berikut adalah penjelasan dari kode tersebut:

 1. Package

```java

package com.joko.springwebflux.model;

```

Kode ini menunjukkan bahwa kelas `Movie` berada di dalam package `com.joko.springwebflux.model`. Package ini digunakan untuk mengelompokkan kelas berdasarkan fungsionalitas yang terkait.

 2. Import Statements

```java

import org.springframework.data.annotation.Id;

import org.springframework.data.mongodb.core.mapping.Document;

import java.util.List;

```

- `@Id`: Anotasi ini digunakan untuk menunjukkan bahwa atribut `id` adalah identifier unik (primary key) untuk objek `Movie`.

- `@Document`: Anotasi ini digunakan untuk memberi tahu Spring bahwa kelas `Movie` akan disimpan sebagai dokumen di MongoDB.

- `List`: Struktur data dari Java yang digunakan untuk menyimpan koleksi elemen (dalam hal ini, genre dari film).


 3. Deklarasi Kelas

```java

@Document

public class Movie {

```

Kelas `Movie` didekorasi dengan anotasi `@Document`, yang mengindikasikan bahwa entitas ini akan disimpan di database MongoDB sebagai sebuah dokumen.

 4. Atribut Kelas

```java

@Id

int id;

String plot;

List<String> genres;

String title;

```

Atribut-atribut berikut terdapat dalam kelas `Movie`:

- `id`: Sebuah integer yang bertindak sebagai identifier unik untuk setiap film.

- `plot`: Sebuah string yang menyimpan ringkasan atau sinopsis dari film.

- `genres`: Sebuah list dari string yang menyimpan genre film (misalnya, "action", "drama", dll).

- `title`: Sebuah string yang menyimpan judul dari film.

 5. Konstruktor

```java

public Movie() {

}


public Movie(int id, String plot, List<String> genres, String title) {

    this.id = id;

    this.plot = plot;

    this.genres = genres;

    this.title = title;

}

```

Terdapat dua konstruktor:

- Konstruktor default (tanpa parameter), yang dibutuhkan oleh framework seperti Spring untuk instansiasi objek.

- Konstruktor yang memiliki parameter untuk menginisialisasi objek `Movie` dengan nilai-nilai tertentu.

 6. Getter dan Setter

Kode ini berisi method getter dan setter untuk setiap atribut kelas:

```java

public int getId() { ... }

public void setId(int id) { ... }

public String getPlot() { ... }

public void setPlot(String plot) { ... }

public List<String> getGenres() { ... }

public void setGenres(List<String> genres) { ... }

public String getTitle() { ... }

public void setTitle(String title) { ... }

```

Getter dan setter digunakan untuk mengambil (get) dan mengubah (set) nilai dari atribut `id`, `plot`, `genres`, dan `title`.

 7. Method `toString()`

```java

@Override

public String toString() {

    return "Movie{" +

            "id=" + id +

            ", plot='" + plot + '\'' +

            ", genres=" + genres +

            ", title='" + title + '\'' +

            '}';

}

```

Method ini digunakan untuk mengubah objek `Movie` menjadi string yang mudah dibaca, yang biasanya berguna untuk debugging atau logging.

Kelas `Movie` ini adalah sebuah entitas yang merepresentasikan film dalam sistem dengan atribut-atribut seperti `id`, `plot`, `genres`, dan `title`. Kelas ini menggunakan anotasi Spring MongoDB (`@Document` dan `@Id`) untuk memetakan objek ini ke dalam dokumen MongoDB.

DAO repository : 

package com.joko.springwebflux.dao;

import com.joko.springwebflux.model.Movie;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import reactor.core.publisher.Flux;

public interface MovieRepository extends ReactiveMongoRepository<Movie, Integer> {

    @Query("{ 'name': ?0 }")
    Flux<Movie> findByName(final String title);

}

Kode yang diberikan merupakan sebuah repository yang digunakan untuk berinteraksi dengan database MongoDB dalam konteks aplikasi berbasis **Spring WebFlux**. Repository ini menggunakan **ReactiveMongoRepository**, yang mendukung operasi secara **asinkron dan non-blocking** sesuai dengan konsep reaktif di Spring WebFlux.

 1. Package

```java

package com.joko.springwebflux.dao;

```

Kode ini menunjukkan bahwa interface `MovieRepository` berada di dalam package `com.joko.springwebflux.dao`, yang kemungkinan besar digunakan untuk menampung class atau interface yang berfungsi sebagai Data Access Object (DAO).

 2. Import Statements

```java

import com.joko.springwebflux.model.Movie;

import org.springframework.data.mongodb.repository.Query;

import org.springframework.data.mongodb.repository.ReactiveMongoRepository;

import reactor.core.publisher.Flux;

```

- `Movie`: Import model `Movie`, yang merupakan entitas yang disimpan di MongoDB.

- `@Query`: Digunakan untuk menulis query MongoDB secara manual di dalam metode repository.

- `ReactiveMongoRepository`: Interface khusus yang disediakan oleh Spring Data MongoDB untuk menangani operasi CRUD dengan sifat reaktif.

- `Flux`: Tipe data reaktif yang disediakan oleh Project Reactor, yang mewakili aliran data yang bersifat non-blocking. Berbeda dari `Mono` (yang hanya mewakili satu elemen atau kosong), `Flux` dapat mengandung 0 hingga banyak elemen.

 3. Deklarasi Interface

```java

public interface MovieRepository extends ReactiveMongoRepository<Movie, Integer> {

```

- `MovieRepository`: Interface ini merupakan **repository** yang digunakan untuk berinteraksi dengan database MongoDB.

- `extends ReactiveMongoRepository<Movie, Integer>`: Interface ini mewarisi dari `ReactiveMongoRepository`, yang menyediakan implementasi default untuk operasi CRUD yang reaktif (misalnya, `findAll()`, `save()`, `delete()`, dll). 

  - `Movie`: Tipe entitas yang akan dikelola oleh repository ini.

  - `Integer`: Tipe dari ID entitas (`Movie`), yang dalam hal ini adalah tipe `Integer`.

 4. Custom Query Method

```java

@Query("{ 'name': ?0 }")

Flux<Movie> findByName(final String title);

```

- `@Query("{ 'name': ?0 }")`: Mengindikasikan bahwa ini adalah custom query manual untuk MongoDB. Dalam query ini, `'name'` adalah field dalam dokumen MongoDB yang ingin dicari, dan `?0` adalah placeholder untuk parameter pertama dari metode (`title`).

  - Field `'name'` ini diasumsikan ada di dalam dokumen `Movie` di MongoDB. Mungkin ada sedikit kesalahan penamaan di sini karena field yang benar di kelas `Movie` adalah `title`, bukan `name`.

- `Flux<Movie>`: Metode ini mengembalikan hasil pencarian berupa aliran reaktif (`Flux`) yang berisi satu atau lebih objek `Movie`.

- `findByName(final String title)`: Nama metode ini secara semantik berarti "mencari film berdasarkan nama/title". Metode ini menerima sebuah parameter `title` (judul film) dan akan mengembalikan semua film yang cocok dengan judul tersebut.

 Kesimpulan:

- MovieRepository` adalah repository yang digunakan untuk mengakses data `Movie` dari MongoDB.

- ReactiveMongoRepository` menyediakan operasi database dasar yang bersifat reaktif, mengikuti pola non-blocking dari Spring WebFlux.

- Custom query dengan anotasi `@Query` memungkinkan penulisan query MongoDB manual untuk mencari film berdasarkan nama (`name`), meskipun berdasarkan model seharusnya adalah `title`.

- Metode `findByName` mengembalikan `Flux<Movie>`, yang merepresentasikan serangkaian hasil pencarian secara reaktif (dapat berisi 0 atau lebih hasil).

Jika ada kesalahan di query, seperti menggunakan `'name'` alih-alih `'title'`, maka sebaiknya diganti agar sesuai dengan field yang ada di kelas `Movie`.

service :

package com.joko.springwebflux.service;

import com.joko.springwebflux.model.Movie;
import com.joko.springwebflux.dao.MovieRepository;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Service
public class MovieService implements IMovieService {

    final
    MovieRepository movieRepo;

    public MovieService(MovieRepository movieRepo) {
        this.movieRepo = movieRepo;
    }

    public void create(Movie e) {
        movieRepo.save(e).subscribe();
    }

    public Flux<Movie> findAll() {
        return movieRepo.findAll();
    }
    public Flux<Movie> findByName(String title) {
        return movieRepo.findByName(title);
    }

    public Mono<Movie> findById(Integer id) {
        return movieRepo.findById(id);
    }

    public Mono<Movie> update(Movie e) {
        return movieRepo.save(e);
    }

    public Mono<Void> delete(Integer id) {
        return movieRepo.deleteById(id);
    }

}

Kode yang diberikan adalah implementasi dari **MovieService**, sebuah service layer dalam aplikasi Spring WebFlux. Kelas ini bertanggung jawab untuk mengelola operasi bisnis terkait objek **Movie**, seperti penyimpanan, pengambilan, pembaruan, dan penghapusan data film dalam database MongoDB, menggunakan repository `MovieRepository`.

 1. Package

```java

package com.joko.springwebflux.service;

```

Kode ini menunjukkan bahwa `MovieService` berada di dalam package `com.joko.springwebflux.service`, yang digunakan untuk mengelompokkan service logic.

 2. Import Statements

```java

import com.joko.springwebflux.model.Movie;

import com.joko.springwebflux.dao.MovieRepository;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import reactor.core.publisher.Flux;

import reactor.core.publisher.Mono;

```

- **`Movie`**: Model atau entitas yang merepresentasikan objek film.

- **`MovieRepository`**: Repository yang berinteraksi dengan MongoDB untuk operasi database terkait `Movie`.

- **`@Service`**: Anotasi ini memberi tahu Spring bahwa kelas `MovieService` ini adalah **service** yang akan dikelola oleh container Spring dan memungkinkan dependency injection.

- **`Flux`** dan **`Mono`**: Tipe data reaktif dari Project Reactor.

  - **`Flux`** digunakan untuk mengelola aliran data yang bisa terdiri dari 0 hingga banyak elemen.

  - **`Mono`** digunakan untuk aliran data yang hanya terdiri dari 0 atau 1 elemen.

 3. Deklarasi Kelas dan Dependency Injection

```java

@Service

public class MovieService implements IMovieService {

```

- **`@Service`**: Menandai bahwa `MovieService` adalah kelas service yang akan dikelola oleh Spring, dan dapat digunakan untuk menjalankan operasi terkait logika bisnis.

- **`implements IMovieService`**: Kelas ini mengimplementasikan interface `IMovieService` (yang tidak disediakan di kode ini), kemungkinan besar berisi definisi abstrak dari metode yang digunakan dalam service ini.

```java

final MovieRepository movieRepo;

```

- `movieRepo`: Dependency `MovieRepository` yang dideklarasikan sebagai `final` untuk memastikan bahwa referensi ini tidak akan diubah setelah diinisialisasi.

```java

public MovieService(MovieRepository movieRepo) {

    this.movieRepo = movieRepo;

}

```

- Konstruktor ini melakukan **constructor-based dependency injection**. `MovieRepository` akan diinject oleh Spring saat service ini dibuat, memberikan akses ke metode CRUD MongoDB.

 4. Method `create(Movie e)`

```java

public void create(Movie e) {

    movieRepo.save(e).subscribe();

}

```

- **`movieRepo.save(e)`**: Menyimpan objek `Movie` ke dalam database MongoDB. Karena `save()` mengembalikan `Mono<Movie>`, method ini bersifat reaktif.

- **`.subscribe()`**: Menginisialisasi eksekusi operasi reaktif. Tanpa `subscribe()`, operasi tidak akan dijalankan. Karena ini adalah operasi non-blocking, `subscribe()` memulai penyimpanan objek tanpa menunggu hasilnya.

 5. Method `findAll()`

```java

public Flux<Movie> findAll() {

    return movieRepo.findAll();

}

```

- **`movieRepo.findAll()`**: Mengambil semua objek `Movie` dari database MongoDB. Operasi ini mengembalikan **`Flux<Movie>`**, karena bisa mengembalikan banyak hasil.

 6. Method `findByName(String title)`

```java

public Flux<Movie> findByName(String title) {

    return movieRepo.findByName(title);

}

```

- **`movieRepo.findByName(title)`**: Mengambil objek `Movie` yang memiliki judul tertentu, menggunakan custom query dari repository (`findByName`). Metode ini mengembalikan **`Flux<Movie>`**, karena bisa ada lebih dari satu film dengan judul yang sama.

 7. Method `findById(Integer id)`

```java

public Mono<Movie> findById(Integer id) {

    return movieRepo.findById(id);

}

```

- **`movieRepo.findById(id)`**: Mencari objek `Movie` berdasarkan ID-nya. Karena ID bersifat unik, operasi ini mengembalikan **`Mono<Movie>`** yang akan berisi 0 atau 1 hasil.

 8. Method `update(Movie e)`

```java

public Mono<Movie> update(Movie e) {

    return movieRepo.save(e);

}

```

- **`movieRepo.save(e)`**: Mengupdate objek `Movie` dengan menyimpan perubahan ke database MongoDB. MongoDB secara otomatis melakukan upsert, artinya jika objek sudah ada, maka akan diperbarui, dan jika belum ada, akan ditambahkan. Metode ini mengembalikan **`Mono<Movie>`**, yang berarti operasi ini hanya bekerja dengan satu entitas.

 9. Method `delete(Integer id)`

```java

public Mono<Void> delete(Integer id) {

    return movieRepo.deleteById(id);

}

```

- **`movieRepo.deleteById(id)`**: Menghapus objek `Movie` berdasarkan ID-nya. Metode ini mengembalikan **`Mono<Void>`**, yang menunjukkan bahwa operasi tidak mengembalikan data apa pun, tetapi hanya memberi sinyal ketika penghapusan selesai.

 Kesimpulan:

- **`MovieService`** adalah layer service yang mengelola operasi CRUD untuk entitas `Movie` secara reaktif.

- Service ini menggunakan **ReactiveMongoRepository** untuk operasi non-blocking dengan MongoDB.

- **Flux** digunakan untuk mengembalikan kumpulan data (seperti pada `findAll()` dan `findByName()`), sedangkan **Mono** digunakan untuk operasi yang hanya mengembalikan satu entitas atau tanpa hasil (seperti `findById()`, `update()`, dan `delete()`).


implementasi service : 

package com.joko.springwebflux.service;

import com.joko.springwebflux.model.Movie;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public interface IMovieService {

    void create(Movie e);
    Flux<Movie> findAll();

    Flux<Movie> findByName(String title);

    Mono<Movie> findById(Integer id);

    Mono<Movie> update(Movie e);

    Mono<Void> delete(Integer id);
}

Kode yang diberikan mendefinisikan sebuah interface bernama `IMovieService`, yang menyediakan kontrak atau abstraksi untuk service terkait operasi CRUD (Create, Read, Update, Delete) pada entitas **Movie** dalam aplikasi berbasis **Spring WebFlux**. Interface ini beroperasi secara **reaktif**, memanfaatkan **Flux** dan **Mono** dari Project Reactor untuk menangani data.

Berikut penjelasan lebih detail dari setiap bagian kode tersebut:

 1. Package

```java

package com.joko.springwebflux.service;

```

Package ini menunjukkan bahwa interface `IMovieService` berada di dalam package `com.joko.springwebflux.service`. Ini mengindikasikan bahwa interface ini adalah bagian dari layer **service** dalam aplikasi.

 2. Import Statements

```java

import com.joko.springwebflux.model.Movie;

import reactor.core.publisher.Flux;

import reactor.core.publisher.Mono;

```

- **`Movie`**: Model atau entitas yang merepresentasikan sebuah film.

- **`Flux`**: Tipe reaktif yang dapat berisi 0 hingga banyak elemen. Digunakan ketika ada lebih dari satu hasil yang diharapkan (misalnya, list film).

- **`Mono`**: Tipe reaktif yang mengandung paling banyak 1 elemen (atau kosong). Digunakan untuk operasi yang mengembalikan satu entitas atau untuk operasi yang tidak memerlukan pengembalian data (seperti penghapusan).

 3. Deklarasi Interface

```java

public interface IMovieService {

```

- **`IMovieService`**: Interface ini mendefinisikan kontrak untuk service yang mengelola entitas `Movie`. Semua class yang mengimplementasikan interface ini harus menyediakan implementasi untuk metode-metode yang didefinisikan.

 4. Metode dalam `IMovieService`

 a. `void create(Movie e)`

```java

void create(Movie e);

```

- **Tujuan**: Metode ini digunakan untuk menambahkan objek `Movie` baru ke dalam database.

- **Parameter**: `Movie e` - objek film yang akan disimpan.

- **Return**: Tidak ada nilai yang dikembalikan (`void`), tetapi karena metode yang sebenarnya diimplementasikan akan bersifat reaktif, penyimpanan mungkin akan dilakukan secara asinkron.

 b. `Flux<Movie> findAll()`

```java

Flux<Movie> findAll();

```

- **Tujuan**: Mengambil semua film dari database.

- **Return**: Mengembalikan **`Flux<Movie>`**, yang merupakan aliran reaktif berisi daftar film. `Flux` dapat berisi 0 hingga banyak elemen.


 c. `Flux<Movie> findByName(String title)`

```java

Flux<Movie> findByName(String title);

```

- **Tujuan**: Mencari film berdasarkan judul.

- **Parameter**: `String title` - judul film yang dicari.

- **Return**: Mengembalikan **`Flux<Movie>`**, yang merupakan aliran reaktif berisi daftar film yang cocok dengan judul yang diberikan. Karena bisa ada lebih dari satu film dengan judul yang sama, hasil dikembalikan dalam bentuk `Flux`.

 d. `Mono<Movie> findById(Integer id)`

```java

Mono<Movie> findById(Integer id);

```

- **Tujuan**: Mencari film berdasarkan **ID**.

- **Parameter**: `Integer id` - ID unik dari film yang ingin dicari.

- **Return**: Mengembalikan **`Mono<Movie>`**, yang merupakan aliran reaktif berisi satu film atau kosong jika film dengan ID tersebut tidak ditemukan.

 e. `Mono<Movie> update(Movie e)`

```java

Mono<Movie> update(Movie e);

```

- **Tujuan**: Mengupdate atau memperbarui data film di database.

- **Parameter**: `Movie e` - objek film yang berisi data yang diperbarui.

- **Return**: Mengembalikan **`Mono<Movie>`**, yang berisi film yang diperbarui setelah perubahan disimpan.

 f. `Mono<Void> delete(Integer id)`

```java

Mono<Void> delete(Integer id);

```

- **Tujuan**: Menghapus film berdasarkan **ID** dari database.

- **Parameter**: `Integer id` - ID dari film yang akan dihapus.

- **Return**: Mengembalikan **`Mono<Void>`**, yang menunjukkan bahwa operasi penghapusan sudah selesai, tetapi tidak mengembalikan data.

 Kesimpulan:

- **`IMovieService`** adalah **interface service layer** yang mendefinisikan operasi dasar yang harus diimplementasikan oleh service yang menangani entitas `Movie`.

- Metode-metode yang dideklarasikan bersifat **reaktif**, menggunakan **Flux** dan **Mono** untuk mendukung aliran data secara asinkron dan non-blocking.

- Interface ini berfokus pada operasi CRUD, termasuk menambah film (`create`), mengambil daftar film (`findAll`), mencari film berdasarkan judul atau ID (`findByName`, `findById`), memperbarui film (`update`), dan menghapus film (`delete`).

controller : 

package com.joko.springwebflux.controller;

import com.joko.springwebflux.model.Movie;
import com.joko.springwebflux.service.MovieService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@RestController
@RequestMapping("/movie")
public class MovieController {

    @Autowired
    private MovieService movieService;

    @PostMapping(path = "/add", consumes = "application/json")
    @ResponseStatus(HttpStatus.CREATED)
    public void create(@RequestBody Movie movie) {
        movieService.create(movie);
    }

    @GetMapping("/get/{id}")
    @ResponseBody
    public ResponseEntity<Mono<Movie>> findById(@PathVariable("id") Integer id) {
        Mono<Movie> movie = movieService.findById(id);
        return new ResponseEntity<Mono<Movie>>(movie, HttpStatus.OK);
    }

    @GetMapping( "/getByTitle")
    public Flux<Movie> findByName(@RequestParam("title") String title) {
        return movieService.findByName(title);
    }

    @GetMapping("/")
    public Flux<Movie> findAll() {
        Flux<Movie> movies = movieService.findAll();
        return movies;
    }

    @PutMapping("/update")
    @ResponseStatus(HttpStatus.OK)
    public Mono<Movie> update(@RequestBody Movie movie) {
        return movieService.update(movie);
    }

    @DeleteMapping( "/delete/{id}")
    @ResponseStatus(HttpStatus.OK)
    public void delete(@PathVariable("id") Integer id) {
        movieService.delete(id).subscribe();
    }
}

config : 

database config : 

package com.joko.springwebflux.config;

import com.mongodb.reactivestreams.client.MongoClient;
import com.mongodb.reactivestreams.client.MongoClients;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.AbstractReactiveMongoConfiguration;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories;


@Configuration
@EnableReactiveMongoRepositories(basePackages = "com.joko.springwebflux.dao")
public class MongoConfig extends AbstractReactiveMongoConfiguration {
    @Value("${port}")
    private String port;

    @Value("${dbname}")
    private String dbName;

    @Override
    public MongoClient reactiveMongoClient() {
        return MongoClients.create();
    }

    @Override
    protected String getDatabaseName() {
        return dbName;
    }

    @Bean
    public ReactiveMongoTemplate reactiveMongoTemplate() {
        return new ReactiveMongoTemplate(reactiveMongoClient(), getDatabaseName());
    }
}


WebFluxConfig : 

package com.joko.springwebflux.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.config.EnableWebFlux;
import org.springframework.web.reactive.config.WebFluxConfigurer;

@Configuration
@EnableWebFlux
public class WebFluxConfig implements WebFluxConfigurer {
}

spring application : 

package com.joko.springwebflux;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringWebFluxApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringWebFluxApplication.class, args);
    }

}

iklan

iklan