Skip to content

Instantly share code, notes, and snippets.

@kaqfa
Last active May 28, 2025 02:33
Show Gist options
  • Select an option

  • Save kaqfa/ccec9f861387e17ba8d66c72a3508f8b to your computer and use it in GitHub Desktop.

Select an option

Save kaqfa/ccec9f861387e17ba8d66c72a3508f8b to your computer and use it in GitHub Desktop.
Tutorial: Membangun Aplikasi Desktop Java dengan JavaFX, SQLite, dan Pola MVVM

Cerita Pembuka: Bayangkan kita adalah seorang developer yang diminta membangun sistem informasi akademik sederhana. Seperti membangun rumah, kita perlu merencanakan arsitektur yang baik sejak awal. Tutorial ini akan mengajak Anda membangun aplikasi step-by-step dengan cara yang terstruktur dan mudah dipahami.

🎯 Tujuan Pembelajaran

Setelah mengikuti tutorial ini, mahasiswa diharapkan mampu:

  • Memahami dan mengimplementasikan pola MVVM (Model-View-ViewModel)
  • Menerapkan DAO Pattern untuk akses database
  • Membuat GUI desktop dengan JavaFX tanpa FXML
  • Mengintegrasikan SQLite database dalam aplikasi Java
  • Menerapkan data binding dan event handling

πŸ› οΈ Prasyarat

  • Java Development Kit (JDK) 21
  • IDE (IntelliJ IDEA, Eclipse, atau VS Code)
  • Pemahaman dasar OOP dan Java
  • Gradle (sudah terinstall dengan project)

πŸ“‹ Step 1: Persiapan Project dan Dependencies

1.1 Struktur Project Gradle

Pertama, kita perlu memahami struktur project yang akan kita bangun:

LatAkademik/
β”œβ”€β”€ app/
β”‚   β”œβ”€β”€ build.gradle          # Konfigurasi dependencies
β”‚   └── src/main/java/latakademik/
β”‚       β”œβ”€β”€ model/           # Data models
β”‚       β”œβ”€β”€ dao/             # Data Access Objects
β”‚       β”œβ”€β”€ database/        # Database utilities
β”‚       β”œβ”€β”€ viewmodel/       # Business logic layer
β”‚       β”œβ”€β”€ view/            # User Interface
β”‚       β”œβ”€β”€ util/            # Helper utilities
β”‚       └── AkademikApp.java # Main application

1.2 Konfigurasi Dependencies di build.gradle

Buka file app/build.gradle dan tambahkan dependencies yang diperlukan:

plugins {
    id 'application'
    id 'org.openjfx.javafxplugin' version '0.1.0'  // Plugin untuk JavaFX
}

dependencies {
    // JavaFX untuk GUI desktop
    implementation 'org.openjfx:javafx-controls:21.0.1'
    implementation 'org.openjfx:javafx-fxml:21.0.1'
    
    // SQLite untuk database
    implementation 'org.xerial:sqlite-jdbc:3.44.1.0'
}

javafx {
    version = '21.0.1'
    modules = ['javafx.controls', 'javafx.fxml']
}

πŸ’‘ Tips: JavaFX tidak lagi bundled dengan JDK sejak Java 11, jadi kita perlu menambahkannya sebagai dependency eksternal.


πŸ“Š Step 2: Membuat Model Classes (Data Layer)

2.1 Analisis Kebutuhan Data

Dalam sistem akademik sederhana, kita memerlukan:

  • Dosen: NPP, Nama, No HP
  • Mahasiswa: NIM, Nama, Gender, IPK, Dosen Wali

2.2 Implementasi Model Dosen

Buat file model/Dosen.java:

package latakademik.model;

public class Dosen {
    private String npp;
    private String nama;
    private String noHp;

    // Constructor, Getters, Setters
    public Dosen(String npp, String nama, String noHp) {
        this.npp = npp;
        this.nama = nama;
        this.noHp = noHp;
    }
    
    // ... getters dan setters
    
    @Override
    public boolean equals(Object obj) {
        // Implementasi berdasarkan NPP sebagai unique identifier
    }
}

2.3 Implementasi Model Mahasiswa

Buat file model/Mahasiswa.java dengan atribut:

  • nim (String) - Primary key
  • nama (String) - Nama lengkap
  • gender (String) - Jenis kelamin
  • ipk (double) - IPK mahasiswa
  • dosenWali (String) - Foreign key ke NPP dosen

πŸ” Analogi: Model classes seperti blueprint rumah. Mereka mendefinisikan struktur data tanpa implementasi logic bisnis.


πŸ—„οΈ Step 3: Database Layer dengan SQLite

3.1 Database Connection Utility

Buat file database/DatabaseConnection.java dengan pattern Singleton:

package latakademik.database;

public class DatabaseConnection {
    private static final String DB_URL = "jdbc:sqlite:akademik.db";
    private static DatabaseConnection instance;
    private Connection connection;

    private DatabaseConnection() {
        try {
            connection = DriverManager.getConnection(DB_URL);
            initializeTables();  // Auto-create tables
        } catch (SQLException e) {
            throw new RuntimeException("Error connecting to database", e);
        }
    }

    public static synchronized DatabaseConnection getInstance() {
        if (instance == null) {
            instance = new DatabaseConnection();
        }
        return instance;
    }
    
    private void initializeTables() {
        createDosenTable();
        createMahasiswaTable();
    }
}

3.2 Schema Database

SQL untuk membuat tabel:

-- Tabel Dosen
CREATE TABLE IF NOT EXISTS dosen (
    npp TEXT PRIMARY KEY,
    nama TEXT NOT NULL,
    no_hp TEXT
);

-- Tabel Mahasiswa
CREATE TABLE IF NOT EXISTS mahasiswa (
    nim TEXT PRIMARY KEY,
    nama TEXT NOT NULL,
    gender TEXT NOT NULL,
    ipk REAL NOT NULL,
    dosen_wali TEXT,
    FOREIGN KEY (dosen_wali) REFERENCES dosen(npp)
);

πŸ—οΈ Analogi: Database Connection seperti jembatan yang menghubungkan aplikasi dengan data storage. Singleton pattern memastikan hanya ada satu koneksi yang aktif.


πŸ”§ Step 4: Implementasi DAO Pattern

4.1 Konsep DAO Pattern

DAO (Data Access Object) memisahkan logic akses data dari business logic. Ini memberikan:

  • Abstraksi: Interface yang clean untuk operasi CRUD
  • Fleksibilitas: Mudah mengganti implementasi database
  • Testability: Mudah untuk unit testing

4.2 Interface DAO

Buat file dao/DosenDAO.java:

package latakademik.dao;

import latakademik.model.Dosen;
import java.util.List;

public interface DosenDAO {
    void save(Dosen dosen);
    void update(Dosen dosen);
    void delete(String npp);
    Dosen findByNpp(String npp);
    List<Dosen> findAll();
}

4.3 Implementasi DAO

Buat file dao/DosenDAOImpl.java:

package latakademik.dao;

public class DosenDAOImpl implements DosenDAO {
    private final Connection connection;

    public DosenDAOImpl() {
        this.connection = DatabaseConnection.getInstance().getConnection();
    }

    @Override
    public void save(Dosen dosen) {
        String sql = "INSERT INTO dosen (npp, nama, no_hp) VALUES (?, ?, ?)";
        try (PreparedStatement stmt = connection.prepareStatement(sql)) {
            stmt.setString(1, dosen.getNpp());
            stmt.setString(2, dosen.getNama());
            stmt.setString(3, dosen.getNoHp());
            stmt.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException("Error saving dosen", e);
        }
    }
    
    // Implementasi method lainnya...
}

πŸ“ Best Practice: Selalu gunakan PreparedStatement untuk mencegah SQL injection dan meningkatkan performa.


🎭 Step 5: Implementasi MVVM Pattern

5.1 Konsep MVVM

MVVM memisahkan concerns menjadi tiga layer:

  • Model: Data dan business rules
  • View: User Interface (JavaFX components)
  • ViewModel: Penghubung antara View dan Model

5.2 ViewModel dengan JavaFX Properties

Buat file viewmodel/DosenViewModel.java:

package latakademik.viewmodel;

import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

public class DosenViewModel {
    private final DosenDAO dosenDAO;
    private final ObservableList<Dosen> dosenList;
    
    // Properties untuk data binding
    private final StringProperty npp = new SimpleStringProperty("");
    private final StringProperty nama = new SimpleStringProperty("");
    private final StringProperty noHp = new SimpleStringProperty("");
    
    // Property untuk selected item
    private final ObjectProperty<Dosen> selectedDosen = new SimpleObjectProperty<>();

    public DosenViewModel() {
        this.dosenDAO = new DosenDAOImpl();
        this.dosenList = FXCollections.observableArrayList();
        loadAllDosen();
    }

    public void saveDosen() {
        if (validateInput()) {
            Dosen dosen = new Dosen(npp.get(), nama.get(), noHp.get());
            dosenDAO.save(dosen);
            loadAllDosen();
            clearForm();
        }
    }
    
    // Getters untuk properties
    public StringProperty nppProperty() { return npp; }
    public StringProperty namaProperty() { return nama; }
    // ... property getters lainnya
}

5.3 Data Binding Advantages

JavaFX Properties memungkinkan:

  • Two-way binding: Perubahan di UI otomatis update ViewModel
  • Observer pattern: UI otomatis update ketika data berubah
  • Validation: Mudah menambahkan validasi real-time

🎨 Step 6: Membuat User Interface dengan JavaFX

6.1 Struktur View Tanpa FXML

Buat file view/DosenView.java yang extends VBox:

package latakademik.view;

import javafx.scene.control.*;
import javafx.scene.layout.VBox;

public class DosenView extends VBox {
    private final DosenViewModel viewModel;
    
    // UI Components
    private TextField nppField;
    private TextField namaField;
    private TableView<Dosen> dosenTable;
    private Button saveButton;

    public DosenView() {
        this.viewModel = new DosenViewModel();
        initializeComponents();
        setupLayout();
        bindProperties();
        setupEventHandlers();
    }

    private void bindProperties() {
        // Two-way binding antara UI dan ViewModel
        nppField.textProperty().bindBidirectional(viewModel.nppProperty());
        namaField.textProperty().bindBidirectional(viewModel.namaProperty());
        
        // Bind table data
        dosenTable.setItems(viewModel.getDosenList());
    }

    private void setupEventHandlers() {
        saveButton.setOnAction(e -> {
            viewModel.saveDosen();
            showSuccessMessage("Data berhasil disimpan!");
        });
    }
}

6.2 TableView Setup

private void setupTable() {
    dosenTable = new TableView<>();
    
    // Kolom NPP
    TableColumn<Dosen, String> nppColumn = new TableColumn<>("NPP");
    nppColumn.setCellValueFactory(new PropertyValueFactory<>("npp"));
    
    // Kolom Nama
    TableColumn<Dosen, String> namaColumn = new TableColumn<>("Nama");
    namaColumn.setCellValueFactory(new PropertyValueFactory<>("nama"));
    
    dosenTable.getColumns().addAll(nppColumn, namaColumn);
}

🎯 Step 7: Main Application Class

7.1 Struktur Main App

Buat file AkademikApp.java:

package latakademik;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TabPane;
import javafx.stage.Stage;

public class AkademikApp extends Application {
    
    @Override
    public void start(Stage primaryStage) {
        // Initialize database
        DatabaseConnection.getInstance();
        
        // Create main layout dengan tabs
        Scene mainScene = createMainScene();
        
        Scene scene = new Scene(mainScene, 1000, 700);
        primaryStage.setTitle("Sistem Akademik");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private Scene createMainScene() {
        BorderPane root = new BorderPane();
        
        // Create header
        VBox header = createHeader();
        root.setTop(header);
        
        // Create main content with tabs
        mainTabPane = createMainTabs();
        root.setCenter(mainTabPane);
        
        // Create footer
        HBox footer = createFooter();
        root.setBottom(footer);
        
        return new Scene(root);
    }

    private VBox createHeader() {
        VBox header = new VBox();
        header.getStyleClass().add("header");
        header.setPadding(new Insets(20));
        
        Label titleLabel = new Label("πŸŽ“ Sistem Informasi Akademik");
        titleLabel.getStyleClass().add("main-title");
        
        Label subtitleLabel = new Label("Manajemen Data Mahasiswa dan Dosen");
        subtitleLabel.getStyleClass().add("subtitle");
        
        header.getChildren().addAll(titleLabel, subtitleLabel);
        return header;
    }

    private TabPane createMainTabs() {
        TabPane tabPane = new TabPane();
        tabPane.setTabClosingPolicy(TabPane.TabClosingPolicy.UNAVAILABLE);
        
        // Tab Data Mahasiswa
        mahasiswaView = new MahasiswaView();
        Tab mahasiswaTab = new Tab("πŸ‘¨β€πŸŽ“ Data Mahasiswa", mahasiswaView);
        
        // Tab Data Dosen
        dosenView = new DosenView();
        Tab dosenTab = new Tab("πŸ‘¨β€πŸ« Data Dosen", dosenView);
        
        tabPane.getTabs().addAll(mahasiswaTab, dosenTab);
        
        return tabPane;
    }

    private HBox createFooter() {
        HBox footer = new HBox();
        footer.getStyleClass().add("footer");
        footer.setPadding(new Insets(10));
        
        Label footerLabel = new Label("© 2024 - Sistem Akademik | Dibuat dengan ❀️ menggunakan JavaFX");
        footerLabel.getStyleClass().add("footer-text");
        
        footer.getChildren().add(footerLabel);
        return footer;
    }
}

🎨 Step 8: Styling dengan CSS

8.1 Inline CSS Styling

Tambahkan method untuk styling di AkademikApp.java:

private String createInlineCSS() {
    return "data:text/css," + """
        .root {
            -fx-font-family: 'Segoe UI', sans-serif;
            -fx-background-color: #f5f5f5;
        }
        
        .button-primary {
            -fx-background-color: #667eea;
            -fx-text-fill: white;
            -fx-font-weight: bold;
        }
        
        .button-primary:hover {
            -fx-background-color: #5a6fd8;
        }
        """;
}

8.2 Responsive Design

// Auto-resize columns
dosenTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);

// Responsive button layout
HBox buttonBox = new HBox(10);
buttonBox.getChildren().addAll(saveButton, updateButton, deleteButton);

πŸ§ͺ Step 9: Data Validation dan Error Handling

9.1 Form Validation

Implementasi validasi di ViewModel:

private boolean validateInput() {
    if (npp.get().trim().isEmpty()) {
        showErrorMessage("NPP tidak boleh kosong!");
        return false;
    }
    
    if (nama.get().trim().isEmpty()) {
        showErrorMessage("Nama tidak boleh kosong!");
        return false;
    }
    
    return true;
}

9.2 Exception Handling

public void saveDosen() {
    try {
        if (validateInput()) {
            Dosen dosen = new Dosen(npp.get(), nama.get(), noHp.get());
            dosenDAO.save(dosen);
            loadAllDosen();
            clearForm();
            showSuccessMessage("Data berhasil disimpan!");
        }
    } catch (Exception e) {
        showErrorMessage("Error: " + e.getMessage());
    }
}

πŸš€ Step 10: Testing dan Deployment

10.1 Sample Data untuk Testing

Buat file util/SampleDataUtil.java:

public class SampleDataUtil {
    public void insertSampleData() {
        // Insert sample dosen
        Dosen[] sampleDosen = {
            new Dosen("NPP001", "Dr. Ahmad Fauzi", "08123456789"),
            new Dosen("NPP002", "Dr. Siti Rahayu", "08234567890")
        };
        
        for (Dosen dosen : sampleDosen) {
            dosenDAO.save(dosen);
        }
    }
}

10.2 Menjalankan Aplikasi

# Compile dan run dengan Gradle
./gradlew run

# Atau build JAR file
./gradlew build

πŸŽ“ Step 11: Best Practices dan Optimizations

11.1 Memory Management

  • Tutup database connection saat aplikasi ditutup
  • Gunakan try-with-resources untuk resource management
  • Avoid memory leaks dengan proper event listener cleanup

11.2 Code Organization

  • Single Responsibility: Setiap class punya satu tanggung jawab
  • Dependency Injection: Inject dependencies melalui constructor
  • Error Handling: Consistent error handling strategy

11.3 Performance Tips

  • Use ObservableList untuk table data
  • Implement lazy loading untuk data besar
  • Cache frequently accessed data

πŸ”„ Step 12: Extending the Application

12.1 Adding New Features

  1. Search Functionality: Tambahkan TextField untuk search
  2. Export Data: Export table data ke CSV/Excel
  3. Data Import: Import data dari file eksternal
  4. User Authentication: Tambahkan login system

12.2 Database Enhancements

  1. Migration Scripts: Untuk update schema
  2. Backup/Restore: Fitur backup database
  3. Connection Pooling: Untuk aplikasi yang lebih besar

🎯 Kesimpulan dan Refleksi

Apa yang Telah Dipelajari:

  1. Arsitektur Aplikasi: MVVM dan DAO patterns
  2. JavaFX Programming: GUI tanpa FXML
  3. Database Integration: SQLite dengan JDBC
  4. Data Binding: JavaFX Properties dan ObservableList
  5. Best Practices: Error handling, validation, styling

Key Takeaways:

  • Separation of Concerns adalah fundamental dalam aplikasi yang maintainable
  • Data Binding membuat UI yang responsive dan reactive
  • Pattern Implementation memudahkan testing dan extensibility
  • User Experience sama pentingnya dengan functionality

Next Steps:

  1. Explore advanced JavaFX features (Charts, Animations)
  2. Learn unit testing dengan JUnit
  3. Implement more design patterns (Observer, Factory)
  4. Study enterprise patterns (Repository, Service Layer)

πŸ“š Referensi dan Resources


Penutup: Seperti membangun rumah, membangun aplikasi memerlukan perencanaan yang matang dan eksekusi yang teliti. Dengan mengikuti tutorial ini, Anda telah belajar membangun aplikasi desktop Java yang terstruktur, maintainable, dan user-friendly. Selamat coding! πŸš€

Happy Coding! πŸ’»βœ¨

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment