Java & MySQL (4) – Menggunakan Data Access Object

Melanjutkan tulisan-tulisan sebelumnya tentang penggunaan database MySQL dalam aplikasi Java, kali ini kita akan belajar tentang Data Access Object (DAO).

Sederhananya, Data Access Object (DAO) adalah “objek” yang menjadi perantara antarmuka aplikasi dengan database. Karena sifatnya sebagai perantara, objek ini harus menyediakan metode-metode yang menunjang proses aplikasi yang berhubungan dengan database.

Proses paling dasar yang umumnya terjadi didalam aplikasi yang hubungannya dengan database antara lain :

  1. Membuat/menambah data (create)
  2. Mengambil/menampilkan data (retrieve)
  3. Memperbaharui data (update)
  4. Menghapus data (delete)

Keempat proses diatas biasa dikenal dengan istilah CRUD. DAO tidak terbatas hanya pada keempat proses tersebut, kita bisa saja menambah proses-proses lain sesuai dengan kasus yang dihadapi.

Contoh Kode Program

Perhatikan kode SQL berikut :

DROP DATABASE IF EXISTS sampledb;
CREATE DATABASE sampledb;
USE sampledb;

CREATE TABLE kontak (
  no INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  namaDepan VARCHAR(18) NOT NULL,
  namaBelakang VARCHAR(18) NOT NULL,
  alamat TEXT,
  noTelepon VARCHAR(18),
  email VARCHAR(32)
);

Struktur tabel diatas dapat dimodelkan menjadi class java seperti berikut :

public class Kontak {

  private int no;
  private String namaDepan;
  private String namaBelakang;
  private String alamat;
  private String noTelepon;
  private String email;

  // setter
  public void setNo(int no) { this.no = no; }
  public void setNamaDepan(String namaDepan) { this.namaDepan = namaDepan; }
  public void setNamaBelakang(String namaBelakang) { this.namaBelakang = namaBelakang; }
  public void setAlamat(String alamat) { this.alamat = alamat; }
  public void setNoTelepon(String noTelepon) { this.noTelepon = noTelepon; }
  public void setEmail(String email) { this.email = email; }

  // getter
  public int getNo() { return this.no; }
  public String getNamaDepan() { return this.namaDepan; }
  public String getNamaBelakang() { return this.namaBelakang; }
  public String getAlamat() { return this.alamat; }
  public String getNoTelepon() { return this.noTelepon; }
  public String getEmail() { return this.email; }

}

Sampai disini, kita bisa lanjutkan dengan membuat class DAO untuk mengakses tabel kontak.

Aplikasi yang kita buat bisa saja berurusan dengan lebih dari 1 tabel, dalam kondisi ini kita akan membutuhkan lebih dari 1 DAO. Jika dalam aplikasi terdapat lebih dari 1 DAO, maka tiap-tiap DAO harus mengikuti standard CRUD yang telah kita tentukan. Standard yang dimaksud disini dapat diterapkan dalam bentuk interface.

public interface DataDAO<T> {
  void doCreate(T t) throws java.sql.SQLException;
  java.util.List<T> doRetrieve() throws java.sql.SQLException;
  void doUpdate(T t) throws java.sql.SQLException;
  void doDelete(T t) throws java.sql.SQLException;
}

Class DAO yang mengimplementasikan interface diatas harus menyediakan setidaknya 4 buah metode yaitu :

  1. doCreate()
  2. doRetrieve()
  3. doUpdate(), dan
  4. doDelete().

Interface diatas dapat diimplementasikan seperti berikut :

import java.sql.*;
import java.util.*;

public class KontakDAO implements DataDAO<Kontak> {

  private Connection koneksi;

  public KontakDAO(Connection koneksi) {
    this.koneksi = koneksi;
  }

  @Override
  public void doCreate(Kontak t) throws SQLException {
    String sql = "INSERT INTO kontak(namaDepan, namaBelakang, alamat, noTelepon, email) VALUES (?, ?, ?, ?, ?)";
    PreparedStatement pstmt = koneksi.prepareStatement(sql);
    pstmt.setString(1, t.getNamaDepan());
    pstmt.setString(2, t.getNamaBelakang());
    pstmt.setString(3, t.getAlamat());
    pstmt.setString(4, t.getNoTelepon());
    pstmt.setString(5, t.getEmail());
    pstmt.executeUpdate();
    pstmt.close();
  }

  @Override
  public List<Kontak> doRetrieve() throws SQLException {
    List<Kontak> temp = new ArrayList<>();
    String sql = "SELECT * FROM kontak";
    Statement stmt = koneksi.createStatement();
    ResultSet rs = stmt.executeQuery(sql);
    while(rs.next()) {
      Kontak k = new Kontak();
      k.setNo(rs.getInt("no"));
      k.setNamaDepan(rs.getString("namaDepan"));
      k.setNamaBelakang(rs.getString("namaBelakang"));
      k.setAlamat(rs.getString("alamat"));
      k.setNoTelepon(rs.getString("noTelepon"));
      k.setEmail(rs.getString("email"));
      temp.add(k);
    }
    rs.close();
    stmt.close();
    return temp;
  }

  @Override
  public void doUpdate(Kontak t) throws SQLException {
    String sql = "UPDATE kontak SET namaDepan=?, namaBelakang=?, alamat=?, noTelepon=?, email=? WHERE no=?";
    PreparedStatement pstmt = koneksi.prepareStatement(sql);
    pstmt.setString(1, t.getNamaDepan());
    pstmt.setString(2, t.getNamaBelakang());
    pstmt.setString(3, t.getAlamat());
    pstmt.setString(4, t.getNoTelepon());
    pstmt.setString(5, t.getEmail());
    pstmt.setInt(6, t.getNo());
    pstmt.executeUpdate();
    pstmt.close();
  }

  @Override
  public void doDelete(Kontak t) throws SQLException {
    String sql = "DELETE FROM kontak WHERE no=?";
    PreparedStatement pstmt = koneksi.prepareStatement(sql);
    pstmt.setInt(1, t.getNo());
    pstmt.executeUpdate();
    pstmt.close();
  }

}

Sampai tahap ini kita sudah memiliki class java yang bertugas untuk menangani proses aplikasi terhadap database khususnya pada tabel kontak. Selebihnya kita dapat menggunakan class tersebut pada program seperti pada contoh berikut :

import java.sql.*;

public class Main {

  static String url = "jdbc:mysql://127.0.0.1:3306/sampledb";
  static String user = "root";
  static String password = "p455w0rd";

  public static void main(String[] args) {
    try {
      DriverManager.registerDriver(new com.mysql.jdbc.Driver());
      Connection koneksi = DriverManager.getConnection(url, user, password);
      Kontak k = new Kontak();
      k.setNamaDepan("John");
      k.setNamaBelakang("Doe");
      k.setAlamat("Kendari");
      k.setNoTelepon("+624013123123");
      k.setEmail("john.doe@sample.com");
      KontakDAO ktkDao = new KontakDAO(koneksi);
      ktkDao.doCreate(k);
    }
    catch(SQLException exc) {
      System.err.println(exc.toString());
    }
  }

}

Java & MySQL (3) – Menggunakan Stored Procedure MySQL dan CallableStatement

Tutorial kali ini akan membahas tentang stored procedure MySQL dan pemanggilannya dalam aplikasi java dengan menggunakan objek CallableStatement.

MySQL Stored Procedure

Stored procedure pada intinya merupakan prosedur yang tersimpan di dalam database. Prosedur tersebut dapat menampung 1 atau lebih pernyataan SQL yang saling berhubungan.

Syntax pembuatan stored procedure adalah sebagai berikut :

CREATE PROCEDURE [nama_prosedur]
(
  IN|OUT param_1 [tipe_data],
  ...
  IN|OUT param_n [tipe_data],
)
BEGIN
  [pernyataan sql 1];
  [pernyataan sql 2];
  [pernyataan sql n];
END

Sebagai contoh, perhatikan script SQL berikut :

DROP DATABASE IF EXISTS sample;
CREATE DATABASE sample;
USE sample;

CREATE TABLE barang (
	kode VARCHAR(18) NOT NULL,
	nama TEXT NOT NULL,
	kategori VARCHAR(32) NOT NULL,
	harga INT NOT NULL,
	jumlah INT NOT NULL DEFAULT 1,
	primary key (kode)
);

CREATE TABLE penjualan (
	no INT NOT NULL AUTO_INCREMENT,
	tanggal TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
	kode_barang VARCHAR(18) NOT NULL,
	jumlah INT NOT NULL DEFAULT 1,
	PRIMARY KEY (no),
	FOREIGN KEY (kode_barang) REFERENCES barang (kode)
);

-- Pembuatan Stored Procedure
DELIMITER &
CREATE PROCEDURE tambah_penjualan (IN _kdBarang VARCHAR(18), IN _jmlBarang INT)
BEGIN
	SET @stok := (SELECT jumlah FROM barang WHERE kode = _kdBarang);
	INSERT INTO penjualan (kode_barang, jumlah) VALUES (_kdBarang, _jmlBarang);
	UPDATE barang SET jumlah = (@stok - _jmlBarang) WHERE kode = _kdBarang;
END &
DELIMITER ;

-- Tambah stok barang
INSERT INTO barang VALUES ("MB001", "Asus X370-F Gaming", "Motherboard", 4500000, 8);

Pada script diatas, pembuatan prosedur dilakukan pada baris 24 sampai 31. Dalam prosedur tersebut kita menggunakan tahapan yang umumnya terjadi dalam transaksi penjualan yaitu, menambah record penjualan kedalam tabel penjualan yang dilanjutkan dengan pengurangan jumlah stok dalam tabel barang.

Prosedur tersebut menggunakan 2 buah parameter yaitu, _kdBarang merupakan kode dari barang yang terjual, dan _jmlBarang yaitu berapa banyak barang yang terjual.

Proses yang terjadi dalam prosedur adalah sebagai berikut :

  1. Mengambil jumlah stok saat ini dari barang yang memiliki kode _kdBarang
  2. Menambah record penjualan kedalam tabel penjualan
  3. Memperbaharui jumlah barang dengan kode _kdBarang yaitu jumlah stok saat ini dikurangi jumlah terjual

Pemanggilan Prosedur

Prosedur diatas dapat dipanggil dengan menggunakan perintah :

CALL tambah_penjualan("MB001", 2);

Prosedur tersebut akan menambah record penjualan barang dengan kode MB001 dan memperbaharui jumlah stok barang MB001 menjadi 6 yaitu hasil pengurangan dari stok saat ini (8) dan jumlah terjual (2)

Pemanggilan Prosedur Melalui Aplikasi Java

Untuk memanggil prosedur MySQL dari aplikasi java, kita dapat menggunakan objek CallableStatement.

Objek CallableStatement terbentuk melalui metode prepareCall() dari objek Connection. Metode prepareCall() menggunakan 1 parameter String yaitu perintah pemanggilan prosedur.

Perhatikan contoh berikut :

import java.sql.*;

public class CallableStatementSample {

  static String url = "jdbc:mysql://127.0.0.1:3306/sample";
  static String user = "root";
  static String password = "";

  public static void main(String[] args) {
    try {
      DriverManager.registerDriver(new com.mysql.jdbc.Driver());
      Connection koneksi = DriverManager.getConnection(url, user, password);
      CallableStatement cstmt = koneksi.prepareCall("{CALL tambah_penjualan(?, ?)}");
      cstmt.setString(1, "MB001");
      cstmt.setInt(2, 2);
      cstmt.executeUpdate();
      cstmt.close();
      koneksi.close();
    }
    catch(SQLException exc) {
      System.err.println(exc.toString());
    }
  }

}

Java & MySQL (2) – Menggunakan PreparedStatement

Melanjutkan tulisan sebelumnya tentang dasar koneksi java dan mysql, pada tulisan kali ini saya akan membahas tentang penggunaan objek PreparedStatement untuk eksekusi query SQL. Untuk lebih memahamkan pembaca, saya akan mencoba menggunakan satu studi kasus sederhana.

Membuat Database

Ketikkan kode SQL berikut, simpan dengan nama dbinventory.sql dan eksekusi menggunakan client MySQL melalui Command Prompt.

DROP DATABASE IF EXISTS dbinventory;
CREATE DATABASE dbinventory;
USE dbinventory;

CREATE TABLE parts (
  kode VARCHAR(12) NOT NULL,
  nama TEXT NOT NULL,
  jumlah INT NOT NULL DEFAULT 1,
  tgl_masuk TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  kondisi ENUM('baik','rusak') NOT NULL DEFAULT 'baik',
  PRIMARY KEY (kode)
);

Import Class

Class java yang perlu kita import adalah sebagai berikut :

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

Registrasi Driver & Koneksi Database

Seperti sebelumnya kita perlu meregistrasi driver dan mengkoneksikan aplikasi ke database

try {
  DriverManager.registerDriver(new com.mysql.jdbc.Driver());
  Connection koneksi = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/namadb", "root", "p455w0rd");
}
catch(SQLException exc) {
  System.err.println(exc.toString());
}

Membuat Objek PreparedStatement

Objek PreparedStatement dibentuk menggunakan metode prepareStatement() dari objek koneksi yang sudah dibentuk sebelumnya.

Metode prepareStatement() menggunakan satu parameter String yaitu query SQL yang akan dieksekusi.

Disini kita coba menambah data baru kedalam tabel parts.

try {
  DriverManager.registerDriver(new com.mysql.jdbc.Driver());
  Connection koneksi = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/namadb", "root", "p455w0rd");
  String sql = "INSERT INTO parts(kode, nama) VALUES (?, ?) ";
  PreparedStatement ps = koneksi.prepareStatement(sql);
}
catch(SQLException exc) {
  System.err.println(exc.toString());
}

Perhatikan penggunaan simbol tanda tanya (?) pada pernyataan SQL diatas. Simbol tersebut akan mewakili nilai kolom dari data baru yang akan kita masukkan ke dalam tabel parts. Simbol tanda tanya pertama akan mewakili nilai untuk kolom kode dan simbol tanda tanya kedua akan mewakili nilai untuk kolom nama. Untuk kolom lain akan menggunakan nilai default sebagaimana disebutkan dalam pembuatan tabel sebelumnya.

Pemberian Nilai dan Eksekusi Pernyataan SQL

Pemberian nilai kolom dilakukan dengan menggunakan metode setXxx(id, value) dari objek PreparedStatement :

  1. xxx digantikan oleh tipe data dari nilai yang akan dimasukkan. Jika semisal tipe data kolom tabel adalah CHAR, VARCHAR, atau TEXT, maka metode yang digunakan adalah setString(). Metode lain yang dapat digunakan adalah setInt(), setFloat(), setDouble(), setBlob(), dan seterusnya
  2. id adalah index parameter dengan tipe integer. Untuk tanda tanya pertama kita gunakan nilai 1, untuk tanda tanya kedua digunakan nilai 2, dst
  3. value adalah nilai dari kolom.

Eksekusi pernyataan SQL dilakukan dengan menggunakan metode executeUpdate().

Perhatikan potongan kode berikut :

String sql = "INSERT INTO parts(kode, nama) VALUES (?, ?) ";
PreparedStatement ps = koneksi.prepareStatement(sql);
ps.setString(1, "MBAS01AM4");                     // kolom kode
ps.setString(2, "ASUS ROG Strix X370-F Gaming");  // kolom nama
ps.executeUpdate();                               // eksekusi pernyataan sql
ps.close();

Untuk mengambil data dari tabel kita gunakan metode executeQuery(), sebagai contoh :

String sql = "SELECT * FROM parts WHERE jumlah < ?";
PreparedStatement ps = koneksi.prepareStatement(sql);
int jumlah = 5;
ps.setInt(1, jumlah);
ResultSet rs = ps.executeQuery();

Contoh Program

Sebagai contoh latihan perhatikan kode program berikut ini :

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.sql.SQLException;
import java.util.Scanner;

public class PreparedStatementSample {

  private static final String url = "jdbc:mysql://127.0.0.1:3306/dbinventory";
  private static final String user = "root";
  private static final String password = "";

  public static void main(String[] args) {
    try {
      DriverManager.registerDriver(new com.mysql.jdbc.Driver());
      Connection c = DriverManager.getConnection(url, user, password);
      String sql = "INSERT INTO parts (kode, nama, jumlah) VALUES (?, ?, ?)";
      PreparedStatement pstmt = c.prepareStatement(sql);
      Statement stmt = c.createStatement();
      Scanner sc = new Scanner(System.in);
      while(true) {
        System.out.println("\nAplikasi Inventarisasi");
        System.out.println("[1] Tambah data");
        System.out.println("[2] Tampilkan data");
        System.out.println("[3] Keluar");
        System.out.print("Pilihan [1/2/3] : ");
        int pil = sc.nextInt();
        switch(pil) {
          case 1:
            tambahData(sc, pstmt);
            break;
          case 2:
            tampilkanData(stmt);
            break;
          case 3:
            System.out.println("\nTerima Kasih");
            System.exit(0);
            break;
        }
      }
    }
    catch(SQLException exc) {
      System.err.println(exc.toString());
    }
  }

  public static void tambahData(Scanner sc, PreparedStatement pstmt) throws SQLException {
    System.out.println("\nTambah data");
    System.out.print("Kode : ");
    String kode = sc.next();
    System.out.print("Nama : ");
    String nama = sc.next();
    System.out.print("Jumlah : ");
    int jumlah = sc.nextInt();
    pstmt.setString(1, kode);
    pstmt.setString(2, nama);
    pstmt.setInt(3, jumlah);
    pstmt.executeUpdate();
  }

  public static void tampilkanData(Statement stmt) throws SQLException {
    System.out.println("\nInventarisasi Saat Ini");
    ResultSet rs = stmt.executeQuery("select * from parts");
    int i = 1;
    while(rs.next()) {
      String kode = rs.getString("kode");
      String nama = rs.getString("nama");
      int jumlah = rs.getInt("jumlah");
      String fmt = "[%s] Kode: %-10s Nama: %-20s Jumlah: %-10s\n";
      System.out.printf(fmt, i, kode, nama, jumlah);
      i++;
    }
  }

}

Java & MySQL (1) – Koneksi database dan Statement

Pada tutorial ini kita akan belajar menghubungkan aplikasi java dengan database MySQL dengan API Java Database Connectivity (JDBC).

Import Class

Terdapat beberapa class yang harus diimport kedalam program java

import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

Registrasi Driver

Driver untuk database MySQL disediakan dalam paket MySQL Connector/J yang bisa didownload dari halaman ini. Ekstrak file .zip atau .tar.gz yang sudah didownload, kemudian tempatkan file-file .jar kedalam CLASSPATH. Untuk registrasi driver sendiri dilakukan dengan menggunakan metode statik DriverManager.registerDriver().

try {
  DriverManager.registerDriver(new com.mysql.jdbc.Driver());
}
catch(SQLException exc) {
  System.err.println("Terjadi error : " + exc.toString());
}

Koneksi ke Database

Untuk membentuk koneksi ke database kita gunakan metode statik DriverManager.getConnection(). Metode ini akan mengembalikan objek Connection.

try {
  DriverManager.registerDriver(new com.mysql.jdbc.Driver());
  Connection koneksi = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/namadb", "root", "p455w0rd");
}
catch(SQLException exc) {
  System.err.println("Terjadi error : " + exc.toString());
}

Membuat Objek Statement

Untuk bisa mengeksekusi perintah SQL, kita membutuhkan objek Statement, objek ini didapatkan dengan menggunakan metode createStatement() dari objek koneksi yang sudah terbentuk sebelumnya.

try {
  DriverManager.registerDriver(new com.mysql.jdbc.Driver());
  Connection koneksi = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/namadb", "root", "p455w0rd");
  Statement stmt = koneksi.createStatement();
}
catch(SQLException exc) {
  System.err.println("Terjadi error : " + exc.toString());
}

Eksekusi Perintah SQL

Perintah SQL terbagi menjadi dua kelompok yaitu :

  1. Perintah Query : adalah perintah-perintah SQL yang mengembalikan hasil seperti SELECT, SHOW, dan DESC
  2. Perintah Update : adalah perintah-perintah SQL yang tidak mengembalikan hasil seperti INSERT, UPDATE, DELETE, dan DROP

Untuk perintah-perintah query kita gunakan metode executeQuery(), sedangkan untuk perintah-perintah SQL update kita gunakan metode executeUpdate(). Kedua metode disediakan dalam objek stmt yang sudah terbentuk sebelumnya.

Eksekusi Perintah Query

Metode executeQuery() akan mengembalikan objek ResultSet.

try {
  DriverManager.registerDriver(new com.mysql.jdbc.Driver());
  Connection koneksi = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/namadb", "root", "p455w0rd");
  Statement stmt = koneksi.createStatement();
  ResultSet rs = stmt.executeQuery("SELECT * FROM namatabel");
}
catch(SQLException exc) {
  System.err.println("Terjadi error : " + exc.toString());
}

Eksekusi Perintah Update

Metode executeUpdate() akan mengembalikan nilai integer, yaitu jumlah row yang terdampak dari perintah SQL.

try {
  DriverManager.registerDriver(new com.mysql.jdbc.Driver());
  Connection koneksi = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/namadb", "root", "p455w0rd");
  Statement stmt = koneksi.createStatement();
  int row = stmt.executeUpdate("INSERT INTO namatabel VALUES ('nilaikolom1', 'nilaikolom2')");
  System.out.println("Jumlah baris baru : " + row); // 1
}
catch(SQLException exc) {
  System.err.println("Terjadi error : " + exc.toString());
}

Memproses ResultSet

Objek ResultSet dapat diproses dengan menggunakan perulangan while. Metode yang digunakan bersama perulangan ini adalah metode next() yang mengembalikan nilai boolean true jika masih terdapat data dalam ResultSet dan false jika sudah tidak ada data dalam ResultSet.

Selebihnya untuk mendapatkan nilai kolom, kita gunakan perintah getXxx() dengan parameter nama atau index kolom, dimana xxx adalah tipe data dari kolom tabel. Misalnya tipe data kolom adalah CHAR, VARCHAR, atau TEXT, kita gunakan metode getString(), semisal tipe data kolom adalah INT kita gunakan metode getInt();

try {
  DriverManager.registerDriver(new com.mysql.jdbc.Driver());
  Connection koneksi = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/namadb", "root", "p455w0rd");
  Statement stmt = koneksi.createStatement();
  ResultSet rs = stmt.executeQuery("SELECT * FROM namatabel");
  while(rs.next()) {
    String nama = rs.getString("nama");
    int umur = rs.getInt("umur");
    System.out.println(nama);
    System.out.println(umur);
  }
}
catch(SQLException exc) {
  System.err.println("Terjadi error : " + exc.toString());
}

Kode Lengkap

Kode selengkapnya sebagai berikut :

// ExcJDBC01.java

import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class ExcJDBC01 {

  public static void main(String[] args) {
    try {
      DriverManager.registerDriver(new com.mysql.jdbc.Driver());
      Connection koneksi = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/namadb", "root", "p455w0rd");
      Statement stmt = koneksi.createStatement();
      ResultSet rs = stmt.executeQuery("SELECT * FROM namatabel");
      while(rs.next()) {
        String nama = rs.getString("nama");
        int umur = rs.getInt("umur");
        System.out.println(nama);
        System.out.println(umur);
      }
    }
    catch(SQLException exc) {
      System.err.println("Terjadi error : " + exc.toString());
    }
  }

}