Memakai dynamic finders milik simple-jpa


Plugin simple-jpa akan menyuntikkan beberapa method dinamis di controller.  Methods yang ada tergolong primitif dan tidak secanggih yang ada di Grails; tapi mungkin bisa memadai untuk kebutuhan ‘sehari-hari’ saya.   Untuk memakai methods yang ada,  saya tinggal memanggil method tersebut.  Karena method tersebut bersifat public, maka saya juga bisa memanggilnya dari view atau model.

Sebagai latihan, saya akan men-install simple-jpa dengan memberikan perintah berikut:

install-plugin simple-jpa

Lalu, saya akan menyiapkan database dengan memberikan perintah berikut ini (saya memakai --skip-database karena saya sudah pernah membuat user dan database sebelumnya):

create-simple-jpa --user=snake --password=12345 --database=latihan --skip-database=true

Kemudian, saya akan membuat dua domain class, yaitu Mahasiswa dan Matakuliah, dengan memberikan perintah berikut ini:

create-domain-class Mahasiswa Matakuliah

Saya akan mengubah kode program Mahasiswa.groovy menjadi seperti berikut ini:

@DomainModel @Entity
@TupleConstructor @ToString
class Mahasiswa {

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

   @Size(min=5, max=150)
   String alamat

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

   @NotEmpty @ManyToMany
   List<Matakuliah> listMatakuliah

}

Saya juga mengubah kode program Matakuliah.groovy menjadi seperti berikut ini:

@DomainModel @Entity
@TupleConstructor @ToString
class Matakuliah {

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

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

}

Kali ini saya tidak akan memakai fasilitas scaffolding, tetapi akan memakai controller yang ada. Saya perlu menambahkan annotation @SimpleJpaTransaction pada controller tersebut.   Saya juga akan meletakkan kode program di method mvcGroupInit(Map args). Ini adalah method yang akan dikerjakan pertama kali saat sebuah MVCGroup dibuat.  Dengan demikian, kode program controller akan terlihat seperti berikut ini:

@SimpleJpaTransaction
class LatihanJpaController {

  def model
  def view

  void mvcGroupInit(Map args) {
     proses()
  }

  def proses = {
     // saya akan menambahkan kode program disini
  }

}

Untuk menyimpan sebuah objek melalui JPA ke tabel, saya perlu memanggil persist() atau merge(). Bila ini adalah sebuah objek baru, maka saya harus menggunakan persist(). Tetapi bila ini adalah detached object, maka saya harus menggunakan merge(). Informasi lebih lanjut mengenai perbedaan ini dapat dibaca di dokumentasi Hibernate JPA.

Sebagai contoh, ini adalah kode program yang menyimpan object:

def proses = {
  Matakuliah matakuliah1 = new Matakuliah("CS001", "Spring Framework")
  Matakuliah matakuliah2 = new Matakuliah("CS002", "Spring Security")
  Matakuliah matakuliah3 = new Matakuliah("CS003", "JPA 2.0")
  Matakuliah matakuliah4 = new Matakuliah("CS004", "Computer Graphic")
  persist(matakuliah1)
  persist(matakuliah2)
  persist(matakuliah3)
  persist(matakuliah4)

  DateTimeFormatter df = DateTimeFormat.forPattern("dd-MM-yyyy")
  Mahasiswa snake = new Mahasiswa("Snake", "Indonesia", df.parseLocalDate("17-02-1987"),
     [matakuliah1, matakuliah2, matakuliah3])
  Mahasiswa steve = new Mahasiswa("Steven", "Indonesia", df.parseLocalDate("01-04-1985"),
     [matakuliah4])
  persist(snake)
  persist(steve)
}

Pada saat dijalankan, Hibernate akan secara otomatis membuat tabel berdasarkan informasi dari domain class.  Hal ini karena pada persistence.xml, nilai hibernate.hbm2ddl.auto adalah create-drop. Baris ini dapat dihapus bila tidak diperlukan. Pada contoh diatas, tabel yang dihasilkan adalah:

  • hibernate_sequence : untuk auto-increment nilai id setiap domain class.
  • mahasiswa
  • matakuliah
  • mahasiswa_matakuliah: untuk hubungan many-to-many antara mahasiswa dan matakuliah.

persist() adalah salah satu method yang disuntik oleh simple-jpa pada controller. Method ini akan memanggil persist() milik EntityManager-nya JPA.

Isi tabel yang ada akan terlihat seperti pada gambar berikut ini:

Isi Tabel Di Database

Isi Tabel Di Database

Walaupun saya hanya menyimpan objek Mahasiswa dan Matakuliah, Hibernate juga mengenali hubungan many-to-many dan secara otomatis menambahkan data di tabel mahasiswa_matakuliah.   Kebanyakan pengguna ORM tidak perlu memusingkan struktur tabel, oleh sebab itu saya perlu lebih berkonsentrasi pada objek ketimbang tabel.

Pada gambar di atas, terlihat bahwa setiap objek secara otomatis memiliki atribut berupa id, createdDate, modifiedDate, dan deleted. Atribut id akan di-isi secara otomatis dan merupakan ‘primary key’ atau identity untuk semua objek yang ada.

Sekarang, saya akan mencoba melakukan pencarian dengan menggunakan dynamic finders.  Tetapi sebelumnya, saya menambahkan sebuah JTextArea di view seperti berikut ini:

application(...) {
  scrollPane() {
    textArea(id: 'output')
  }
}

Sebagai contoh, saya menambahkan kode program ini di closure proses:

def proses = {
   // ...
   // kode program yang membuat dan menyimpan objek diabaikan
   // ...

   view.output.append("Seluruh Object Matakuliah:\n")
   findAllmatakuliah().each { view.output.append("$it\n") }
   view.output.append("\nSeluruh Object Mahasiswa:\n")
   findAllMahasiswa().each { view.output.append("$it\n") }
}

Tampilan program di atas akan terlihat seperti berikut ini:

Hasil findAllModel()

Hasil findAllModel()

Method findAllMatakuliah() dan findAllMahasiswa() adalah salah satu dynamic finders yang disediakan oleh simple-jpa. Saya akan menyebutnya sebagai findAllModel() karena seluruh method yang diawali dengan ‘findAll’ lalu di-ikuti dengan nama sebuah domain class akan mengembalikan List yang berisi seluruh instance untuk domain class tersebut.

Contoh dynamic finder lainnya adalah findModelByAttribute(), misalnya findMahasiswaByNama() atau findMatakuliahByKode(). Berikut ini adalah contoh penggunaannya:

def proses = {
   // ...
   // kode program yang membuat dan menyimpan objek diabaikan
   // ...

   view.output.append("Object Matakuliah:\n")
   findMatakuliahByKode("CS003").each { view.output.append("$it\n") }
   view.output.append("\nObject Mahasiswa:\n")
   findMahasiswaByNama("Snake").each { view.output.append("$it\n") }
}

Tampilan program tersebut akan terlihat seperti pada gambar berikut ini:

Hasil findModelByAttribute()

Hasil findModelByAttribute()

Untuk melakukan pencarian yang memanfaatkan operator JPA, saya dapat menggunakan findModelByAttribute(operator,value) seperti pada contoh berikut ini:

def proses = {
   // ...
   // kode program yang membuat dan menyimpan objek diabaikan
   // ...

   view.output.append("Object Matakuliah:\n")
   findMatakuliahByNama("like", "Spring%").each { view.output.append("$it\n") }
}

Hasilnya akan terlihat seperti pada gambar berikut ini:

Hasil findModelByAttribute(operator,value)

Hasil findModelByAttribute(operator,value)

Untuk pencarian yang fleksibel, saya dapat melakukan pencarian melalui DSL, seperti yang terlihat pada contoh berikut ini:

def proses = {
   // ...
   // kode program yang membuat dan menyimpan objek diabaikan
   // ...

   view.output.append("Object Matakuliah:\n")
   findMatakuliahByDsl {
      nama like ('Spring%')
      or()
      nama equal ('JPA 2.0')
   }.each {
      view.output.append("$it\n")
   }
}

Hasilnya akan terlihat seperti pada gambar berikut ini:

Hasil findModelByDsl

Hasil findModelByDsl

Untuk kondisi dimana dynamic finders bawaan simple-jpa tidak dapat dipakai, saya dapat menggunakan executeQuery() untuk mengerjakan JP QL dan executeNativeQuery() untuk mengerjakan SQL. Sebagai contoh, berikut ini adalah kode program yang mengerjakan JP QL:

def proses = {
   // ...
   // kode program yang membuat dan menyimpan objek diabaikan
   // ...

   view.output.append("Object Mahasiswa:\n")
   executeQuery("FROM Mahasiswa m WHERE YEAR(m.tanggalLahir) = 1985").each {
      view.output.append("$it\n")
   }
}

Program di atas akan mengembalikan objek Mahasiswa bernama Steven. Versi SQL dari JP QL di atas adalah:

def proses = {
   // ...
   // kode program yang membuat dan menyimpan objek diabaikan
   // ...

   commitTransaction()
   view.output.append("Object Mahasiswa:\n")
   executeNativeQuery("SELECT m.id, m.nama FROM Mahasiswa m WHERE YEAR(m.tanggalLahir) = 1985").each { baris ->
      view.output.append("${baris[0]} ${baris[1]}\n")
   }
}

Perhatikan bahwa pada versi executeNativeQuery(), hasil yang dikembalikan bukan dalam bentuk object, melainkan array yang berisi data setiap kolom.  Selain itu, karena SQL akan langung dikerjakan terhadap database tanpa memeriksa persistence context, maka saya wajib memberikan commitTransaction() untuk menerapkan perubahan di database.  Secara default, simple-jpa hanya akan melakukan commit bila sudah closure sudah selesai dikerjakan.

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: