Panduan Scaffolding Di plugin simple-jpa 0.3


Plugin simple-jpa 0.4 dapat di-install dengan memberikan perintah install-plugin simple-jpa 0.4

Salah satu perubahan yang cukup penting di simple-jpa versi 0.3 adalah fasilitas scaffolding-nya. Kini simple-jpa akan menghasilkan dialog untuk asosiasi one-to-many dan one-to-one. Selain itu, pengguna juga tidak perlu lagi mengubah startup group di Application.groovy secara manual. Pada artikel ini, saya akan menuliskan panduan memakai fasilitas scaffolding yang ada.

Membuat Domain Class Dengan create-domain-class

Domain class selaku JPA entity adalah sebuah class biasa yang wajib mengandung annotation @Entity. Selain itu, simple-jpa juga mensyaratkan penggunaan annotation @DomainModel. Annotation tersebut akan menambahkan beberapa atribut pada class misalnya atribut id, deleted, createdDate, dan modifiedDate.

Saat ini, domain class bukanlah artifact Griffon, tidak seperti pada plugin resminya yang belum dirilis. Oleh sebab itu, mereka dikenali berdasarkan sebuah lokasi package tertentu (defaultnya adalah package domain). Bila ingin mengganti lokasi package ini menjadi lokasi lain, tambahkan baris berikut ini pada file Config.groovy:

griffon.simplejpa.model.package = "com.snake.domain"

Agar tidak ada yang terlupa, sebaiknya pengguna menggunakan perintah generate-all untuk menghasilkan template domain class yang nantinya tinggal di-isi.

Sebagai contoh, perintah berikut ini:

create-domain-class Transaksi ItemTransaksi Suplier

akan menghasilkan tiga buah class, yaitu Transaksi, ItemTransaksi, dan Suplier. Ketiga class tersebut akan dihasilkan di lokasi package untuk domain class (bila nilai griffon.simplejpa.model.package diubah, maka class akan dihasilkan di lokasi tersebut).

Selain membuat class, perintah generate-all juga mendaftarkan class yang dihasilkan ke dalam file persistence.xml sehingga Hibernate JPA dapat menemukan domain class tersebut.

Berikut ini adalah contoh sebuah domain class yang dihasilkan oleh create-domain-class:

package com.snake.domain

import ...

@DomainModel @Entity @Canonical
class Transaksi {

}

Annotation @Canonical akan secara otomatis membuat constructor, toString, equals dan hashCode.

PENTING: Bila sebuah domain class memiliki hubungan bidirectional atau hubungan dua arah, maka annotation @Canonical harus mengabaikan salah satu sisi hubungan yang ada. Bila hal ini tidak lakukan, @Canonical akan melakukan proses rekursif hingga terjadi stack overflow. Berikut ini adalah contoh cara mengabaikan atribut di @Canonical:

@DomainModel @Entity @Canonical(excludes="listItemTransaksi")
class Transaksi {
   ...
}

PENTING: Pada konfigurasi default di simple-jpa, tabel atau skema database akan dibuat berdasarkan domain class. Bila domain class memiliki asosiasi dengan domain class lainnya, maka foreign key akan dihasilkan untuk tabel bersangkutan. Hal ini dapat menyebabkan proses penghapusan tabel atau data menjadi gagal karena tabel atau data sekarang harus dihapus secara berurutan sementara Hibernate tidak menghapus tabel berdasarkan urutan tersebut. Tabel atau skema database yang tidak sinkron dengan domain class dapat menyebabkan keanehan pada saat aplikasi dijalankan. Pengguna dapat menghapus tabel secara manual dengan menggunakan perintah MySQL berikut ini:

set foreign_key_checks=0;
drop table transaksi;
drop table itemtransaksi;
drop table suplier;

Melakukan Scaffolding Dengan generate-all

Perintah generate-all akan menghasilkan MVCGroup (berisi model, view dan controller) untuk memanipulasi sebuah domain class. Sebagai contoh, perintah berikut ini akan menghasilkan MVCGroup bernama transaksi:

generate-all Transaksi

akan menghasilkan file TransaksiModel.groovy, TransaksiView.groovy, dan TransaksiController.groovy. Selain itu, untuk membantu pengujian, perintah tersebut juga membuat file TransaksiTest.groovy di lokasi integration\project. Ia juga akan membuat file data.xls bila belum ada. File spreadsheet ini dapat di-isi dengan data yang akan ditambahkan ke tabel selama proses pengujian nanti.

Perintah generate-all juga membuat beberapa file *.properties di lokasi /griffon-app/i18n. Perubahan pada isi file tersebut akan mengubah label dan pesan yang ditampilkan oleh view.

Selain itu, perintah generate-all juga akan mendaftarkan MVC group baru pada file Application.groovy seperti berikut ini:

mvcGroups {
   'transaksi' {
   		model = 'project.TransaksiModel'
        view = 'project.TransaksiView'
        controller = 'project.TransaksiController'
   }
}

Secara default, model, view, dan controller akan dihasilkan di package bernama project. Bila ingin menggunakan package lain, berikan perintah seperti berikut ini:

generate-all Transaksi --generated-package=com.snake

Setiap view yang dihasilkan oleh generate-all dapat ditampilkan secara langsung. Hal ini karena view selalu berada dalam node application(). Untuk secara otomatis mengubah startup group pada saat proses scaffolding, berikan perintah berikut ini:

generate-all Transaksi --set-startup

Bila program dijalankan, view untuk MVCGroup Transaksi akan langsung ditampilkan, seperti yang terlihat pada gambar berikut ini:

Tampilan Hasil Scaffolding

Tampilan Hasil Scaffolding

Bila terjadi perubahan pada kode program domain class dikemudian hari, hasil scaffolding tidak akan berubah. Hal ini sesuai dengan fungsi scaffolding yaitu sebagai kerangka atau alat bantu di masa-masa awal pengembangan aplikasi. Perintah generate-all yang diberikan terhadap domain class yang sudah dibuat sebelumnya tidak akan menyebabkan perubahan. Hal ini untuk mencegah terjadinya hilangnya konten file secara tidak disengaja. Bila pengguna yakin ingin menulis ulang file yang telah di-generate, ia dapat memberikan perintah seperti berikut ini:

generate-all Transaksi --force-overwrite

TIPS: Untuk melihat perubahan yang terjadi atau mengembalikan ke semula akibat penggunaan --force-overwrite, pengguna dapat menggunakan fitur history yang ada pada kebanyakan IDE modern. Sebagai contoh, pada IntelliJ IDEA, dengan men-klik kanan pada editor dan memilih Local History, Show History akan menampilkan riwayat perubahan seperti yang terlihat pada gambar berikut ini:

Local History Di IntelliJ IDEA

Local History Di IntelliJ IDEA

Untuk melakukan scaffolding beberapa domain class dalam satu kali pemanggilan, berikan perintah seperti berikut ini:

generate-all Transaksi ItemTransaksi Suplier

Bila pengguna ingin melakukan scaffolding untuk seluruh domain class yang ada, maka ia bisa memberikan perintah seperti berikut ini:

generate-all *

Pada contoh di atas, walaupun membuat scaffolding untuk seluruh domain class yang ada, tetap hanya akan ada satu MVCGroup yang ditampilkan setiap kali program dijalankan. Bila pengguna ingin merangkai seluruh MVCGroup untuk domain class tersebut ke dalam sebuah halaman utama, maka ia bisa memberikan perintah seperti berikut ini:

generate-all --startup-group=MainGroup

Perintah di atas akan membuat sebuah MVCGroup baru dengan nama MainGroup. Selain itu, perintah di atas juga akan mengubah startup group di Application.groovy menjadi mainGroup sehingga MVCGroup tersebut akan dipanggil setiap kali program dijalankan. Tampilan program bila dijalankan sekarang akan terlihat seperti pada gambar berikut ini:

Tampilan Startup Group

Tampilan Startup Group

Untuk melakukan scaffolding domain class dan membuat startup group dalam satu kali pemanggilan, pengguna bisa memberikan perintah seperti pada gambar berikut ini:

generate-all Transaksi ItemTransaksi Suplier --startup-group=MainGroup
// atau
generate-all * --startup-group=MainGroup

Scaffolding Relasi One To One

Sebagai contoh, berikut ini adalah domain class yang memiliki relasi one-to-one:

@DomainModel
@Entity
@Canonical
class Pegawai {

    @Size(min=2, max=50)
    String nama

    @OneToOne(cascade=CascadeType.ALL, orphanRemoval=true)
    Alamat alamat

}

@DomainModel
@Entity
@Canonical
class Alamat {

    String alamat1

    String alamat2

    String alamat3

}

Bila pengguna memberikan perintah:

generate-all Pegawai

maka simple-jpa akan menghasilkan 2 MVCGroup yaitu pegawai dan alamatAsPair. MVCGroup pegawai adalah MVCGroup untuk melakukan operasi CRUD, sementara itu MVCGroup alamatAsPair dipakai sebagai dialog untuk mengisi nilai alamat (atribut yang memiliki relasi one-to-one).

Program akan terlihat seperti pada gambar berikut ini:

Hasil Scaffolding Untuk Relasi One-To-One

Hasil Scaffolding Untuk Relasi One-To-One

Pada saat tombol Update di-klik di MVCGroup alamatAsPair, TIDAK akan ada operasi penyimpanan objek Alamat ke database. Yang terjadi adalah nilai alamat milik Pegawai akan diatur sesuai dengan yang di-isi oleh pengguna. Proses penyimpan ke database hanya akan terjadi bila tombol Save di-klik di view Pegawai.

PENTING: Karena proses penyimpanan hanya dilakukan pada satu sisi, pastikan bahwa di definisi class untuk sisi tersebut (pada contoh di atas adalah di Pegawai) memiliki atribut cascade seperti berikut ini:

@OneToOne(cascade=CascadeType.ALL, orphanRemoval=true)

PENTING: Pada relasi one-to-one bidirectional, Hibernate JPA mungkin akan menghasilkan tabel yang dilengkapi contrainst foreign-key yang saling merujuk ke tabel lainnya. Hal ini dapat menyebabkan tabel dan data tidak dapat dihapus. Solusinya adalah dengan menghapus foreign-key di kedua tabel secara manual, atau memakai fasilitas soft-delete milik simple-jpa sehingga proses hapus tetap berfungsi.

PENTING: Jangan lupa menambahkan excludes pada annotation @Canonical bila ini adalah hubungan bidirectional.

TIPS: Seluruh MVCGroup yang mengandung “AsPair” tidak akan melakukan operasi CRUD ke database. Dengan demikian, mereka hanya mewakili representasi data di memori. Sifat ini membuat mereka bisa dengan aman mengandung relasi ke domain class lainnya. Sebagai contoh, definsi domain class terlihat seperti berikut ini:

@DomainModel
@Entity
@Canonical
class Pegawai {

    @Size(min=2, max=50)
    String nama

    @OneToOne(cascade=CascadeType.ALL, orphanRemoval=true)
    Alamat alamat

}

@DomainModel
@Entity
@Canonical
class Alamat {

    String alamat1

    String alamat2

    String alamat3

    @ManyToOne
    Kota kota

    @OneToMany(cascade=CascadeType.ALL, orphanRemoval=true)
    List<NomorTelepon> listNomorTelepon

}

@DomainModel
@Entity
@Canonical
class Kota {

    @Size(min=2, max=2)
    String kode

    @Size(min=3, max=50)
    String nama

}

@DomainModel
@Entity
@Canonical
class NomorTelepon {

    String nomorTelepon

}

Pada contoh di atas, Pegawai memiliki relasi one-to-one dengan Alamat. Sebuah Alamat memiliki relasi many-to-one dengan Kota. Alamat juga memiliki relasi one-to-many dengan NomorTelepon (ini bukan contoh rancangan yang baik karena sebenarnya NomorTelepon tidak perlu dibuat menjadi domain class tersendiri!).

Untuk melakukan scaffolding, pengguna memberikan perintah seperti berikut ini:

generate-all * --force-overwrite

Bila pengguna sudah pernah menjalankan program dengan domain class sebelumnya, ia perlu menghapus seluruh tabel yang ada dari database secara manual. Selain itu, bila pengguna ingin tampilan menu di toolbar diperbaharui (karena jumlah domain class bertambah), ia perlu memberikan perintah generate-all --startup-group=MainGroup --force-overwrite.

Sekarang, bila program dijalankan, akan terlihat seperti pada gambar berikut ini:

Hasil Scaffolding Untuk Relasi Di Dalam Relasi

Hasil Scaffolding Untuk Relasi Di Dalam Relasi

Scaffolding Relasi One To Many

Berikut ini adalah contoh domain class yang memiliki relasi one-to-many:

@DomainModel
@Entity
@Canonical
class Master {

    @Size(min=2, max=5)
    String kode

    @Type(type="org.jadira.usertype.dateandtime.joda.PersistentDateTime")
    DateTime tanggal

    @OneToMany(cascade=CascadeType.ALL, orphanRemoval=true)
    List<Detail> listDetail = []

}

@DomainModel
@Entity
@Canonical
class Detail {

    @Size(min=2, max=50)
    String name

}

Bila pengguna memberikan perintah:

generate-all Master

maka simple-jpa akan menghasilkan 2 MVCGroup yaitu master dan detailAsChild. MVCGroup master adalah MVCGroup untuk melakukan operasi CRUD, sementara itu MVCGroup detailAsChild dipakai sebagai dialog untuk mengisi nilai listDetail (atribut yang memiliki relasi one-to-many).

Program akan terlihat seperti pada gambar berikut ini:

Hasil Scaffolding Untuk Relasi One To Many

Hasil Scaffolding Untuk Relasi One To Many

Pada saat tombol Update di-klik di MVCGroup detailAsChild, TIDAK akan ada operasi penyimpanan objek List<Detail> ke database. Yang terjadi adalah nilai listDetail milik Master akan diatur sesuai dengan data yang di-isi di detailAsChild. Proses penyimpan ke database hanya akan terjadi bila tombol Save di-klik di view Master.

PENTING: One-to-many bidirectional lebih disarankan ketimbang memakai one-to-many unidirectional karena tidak membutuhkan tabel baru untuk menampung asosiasi.

PENTING: Pengguna perlu memastikan bahwa List, Set, Map, dan Collection lainnya yang dipakai sudah dibuat instance barunya, seperti pada contoh berikut ini:

List<Detail> listDetail = []  
// atau
List<Detail> listDetail = new ArrayList()

PENTING: Karena proses penyimpanan hanya dilakukan pada satu sisi, pastikan bahwa di definisi class untuk sisi tersebut (pada contoh di atas adalah di Master) memiliki atribut cascade seperti berikut ini:

@OneToMany(cascade=CascadeType.ALL, orphanRemoval=true)

TIPS: Seluruh MVCGroup yang mengandung “AsChild” tidak akan melakukan operasi CRUD ke database. Dengan demikian, mereka hanya mewakili representasi data di memori. Sifat ini membuat mereka bisa dengan aman mengandung relasi ke domain class lainnya. Lihat contoh di relasi one-to-one di atas!

Scaffolding Relasi Many To One

Sebagai contoh, berikut ini adalah domain class yang memiliki relasi many-to-one:

@DomainModel
@Entity
@Canonical
class Pelanggan {

    @Size(min=2, max=50)
    String nama

}

@DomainModel
@Entity
@Canonical
class Transaksi {

    @Size(min=2, max=50)
    String kode

    @Type(type="org.jadira.usertype.dateandtime.joda.PersistentLocalDate")
    LocalDate tanggal

    @ManyToOne
    Pelanggan pelanggan

}

Bila pengguna memberikan perintah:

generate-all Transaksi

Relasi many-to-one akan ditampilkan dalam bentuk sebuah combo box dimana pengguna dapat memilih salah satu Pelanggan yang ada, seperti yang terlihat pada gambar berikut ini:

Hasil Scaffolding Untuk Relasi Many To One

Hasil Scaffolding Untuk Relasi Many To One

Untuk mengisi data Pelanggan, pengguna tetap perlu membuat MVCGroup tersediri dengan menggunakan perintah berikut ini:

generate-all Pelanggan

PENTING: Bila sebuah objek Pelanggan sedang direferensikan oleh minimal sebuah Transaksi, maka objek Pelanggan tersebut tidak dapat dihapus di MVCGroup pelanggan. Salah satu solusinya adalah dengan menggunakan fitur soft-delete yang disediakan oleh simple-jpa.

Scaffolding Relasi Many To Many

Sebagai contoh, berikut ini adalah domain class yang memiliki relasi many-to-many:

@DomainModel
@Entity
@Canonical
class Item {

    @Size(min=5, max=5)
    String kode

    @Size(min=2, max=50)
    String name

    @ManyToMany
    List<Fasilitas> listFasilitas

}

@DomainModel
@Entity
@Canonical(excludes="listItem")
class Fasilitas {

    @Size(min=2, max=2)
    String kode

    @Size(min=2, max=10)
    String nama

    @ManyToMany(mappedBy="listFasilitas")
    List<Item> listItem

}

Bila pengguna memberikan perintah:

generate-all Item Fasilitas

maka relasi many-to-many akan diwakili dengan komponen tagChooser() bawaan simple-jpa yang terlihat seperti pada gambar berikut ini:

Hasil Scaffolding Untuk Relasi Many To Many

Hasil Scaffolding Untuk Relasi Many To Many

PENTING: Jangan lupa menambahkan excludes di annotation @Canonical pada salah satu sisi di relasi bidirectional untuk menghindari proses rekursif.

PENTING: Pada relasi many-to-many bidirectional, yang dapat menyimpan domain class lainnya secara langsung adalah sisi yang bukan inverse (bukan domain class yang memiliki atribut mappedBy).

Perihal Solid Snake
I'm nothing...

Apa komentar Anda?

Please log in using one of these methods to post your comment:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s

%d blogger menyukai ini: