Jumat, 21 September 2012

Pertimbangan dalam pemetaan

Seperti sudah disinggung sekilas dalam bagian sebelumnya, ada beberapa ketidak sesuaian (mismatch) antara desain objek dan desain database. Hubungan antar tabel di database jauh lebih sederhana daripada hubungan antar class di aplikasi berorientasi objek.

Dalam kaitannya dengan hubungan antar class, ada beberapa hal yang harus kita perhatikan, yaitu:

  • kasta class
  • navigasi
  • jenis collection
  • optionality
  • transitive persistence

Mari kita bahas satu persatu.


Kasta

JPA mengenal tiga kasta object: entity, component, dan value type. Mari kita lihat contohnya.

 


Pada domain model di atas, kita lihat seorang Author memiliki informasi nomer yang bisa dihubungi. Karena jaman sudah semakin canggih, satu orang bisa dihubungi di banyak nomer, yaitu nomer telepon rumah, kantor, ponsel GSM, ponsel CDMA, dan juga nomer fax. Untuk mengelompokkan semua nomer ini, kita membuat satu class khusus yaitu ContactNumber.

Pemetaan objek seperti ini disebut dengan istilah fine-grained. Artinya, informasi dikelompokkan secara detail dan spesifik. Tiap class memiliki informasi yang terinci. Karena class adalah tipe data, bisa dikatakan bahwa kita telah membuat tipe data baru, yaitu ContactNumber.

Pertanyaannya, bagaimana kita akan menyimpan data ini ke database? Database tidak mengenal tipe data khusus. Kita hanya bisa menggunakan tipe data yang telah disediakan seperti INT, atau VARCHAR. Kita tidak bisa membuat tipe data baru ContactNumber dalam database.

Ada tiga alternatif yang bisa kita gunakan untuk memetakan ContactNumber ke database. Tiga alternatif ini mencerminkan kasta object dalam JPA.

1. Kasta tertinggi, adalah Entity object. Sebuah Entity memiliki tabel sendiri, primary key sendiri, dan    
    kehidupan sendiri. Class Author adalah sebuah Entity.
2. Kasta di bawah Entity disebut Component. Dia memiliki tabel sendiri dan kadang-kadang memiliki primary key sendiri. Tetapi, Component hidup menempel pada Entity dan tidak bisa berdiri sendiri. Kalau inang Entitynya dihapus, maka Component akan ikut dihapus dari database. Class ContactNumber adalah contoh Component.
3. Kasta paling rendah disebut Value Type. Sama dengan Component, hidupnya menempel pada Entity. Value Type tidak memiliki tabel sendiri. Informasi yang dimilikinya disimpan pada tabel induknya. Daftar email yang dimiliki Author (List<String> emails) adalah contoh Value Type.

Ada beberapa kriteria yang bisa digunakan untuk menentukan kasta suatu object. Dengan menggunakan kriteria ini, kita dapat memetakan desain object ke desain relasional dengan baik. Berikut kriterianya:

1. Shared Reference.

Apakah object tertentu mempunyai referensi ke object lain? Pada contoh kita di atas, class Author memiliki hubunganb dengan class User dan class Article. Oleh karena itu, jelas bahwa class Author akan menjadi Entity.



Di database, tabel Author dan User akan terlihat seperti ini. Class ContactNumber hanya digunakan oleh class Author. Class ini bahkan tidak memiliki referensi ke class induknya, yaitu class Author. Ini ditunjukkan oleh hubungan composition antara class Author dan ContactNumber.

Oleh karena itu, data ContactNumber dimasukkan ke dalam tabel yang sama dengan data Author, seperti dapat dilihat pada gambar di atas.

2. Kehidupan.

Apakah suatu object memiliki kehidupan yang terpisah dari object yang berhubungan dengannya? Data Author masih akan tetap tersimpan walaupun Article yang ditulisnya dihapus.

Tidak demikian halnya dengan ContactNumber. Begitu data Author dihapus, data ContactNumber tidak memiliki relevansi lagi.

Object yang memiliki kehidupan sendiri merupakan kandidat Entity.

3. Jumlah

Untuk memahami pentingnya jumlah dalam pemetaan, mari kita lihat hubungan antara Article dan Comment.



Satu Article memiliki beberapa Comment. Sama dengan ContactNumber, kehidupan Comment juga sangat tergantung pada Article. Kalau Article dihapus, Comment tidak lagi relevan. Apa yang mau dikomentari kalau artikelnya tidak ada?

Satu Author hanya memiliki satu ContactNumber, tapi satu Article memiliki banyak Comment. Perbedaan jumlah ini akan menentukan susunan tabelnya. Berikut susunan tabel Article dan Comment.



Dengan mempertimbangkan ketiga hal di atas, berikut adalah kasta untuk masing-masing object dalam desain domain model kita.

Article =>Entity
Author =>Entity
Category => Entity
Comment => Component
ContactNumber => Value Type
Group => Entity
User => Entity
UserLevel => Value Type

Navigasi
Kita harus memikirkan urusan arah navigasi (direction) dalam merancang pemetaan. Dua class dikatakan memiliki navigasi dua arah (bidirectional) apabila kita dapat mengakses class yang satu dari class yang lain, dan sebaliknya. Sebagai contoh, User dan Group mempunyai hubungan bidirectional, jadi kode Javanya kirakira seperti ini.

public class User {
private Set<Group> groups = new HashSet<Group>();
}


sedangkan class Group berisi seperti ini

public class Group {
private Set<User> members = new HashSet<User>();
}


User dan Author unidirectional, artinya dari class User kita dapat mengakses class Author. Tapi dari class Author kita tidak dapat mengakses class User. Berikut kode Javanya.

public class User {
private Author author;
}


dalam class Author, tidak ada property User.

public class Author {
// tidak ada property User
}


Urusan navigasi ini hanya ada di kode Java. Apapun desain navigasi yang kita buat, tidak mempengaruhi struktur tabel kita.

Jenis Collection
Dalam desain kita di atas, ada beberapa hubungan one-to-many dan many-to-many. Urusan many ini direpresentasikan dalam Java dengan menggunakan Collections Framework. Kita mengenal beberapa interface dalam Collections Framework dengan sifatnya masing-masing:

• Set : Objek yang disimpan dalam Set tidak boleh ada yang sama. Set tidak bisa mengingat urutan. Jadi kita tidak bisa mengharapkan objek yang dimasukkan pertama akan muncul pertama juga. Set biasanya diinisialisasi dengan menggunakan implementasi HashSet.

• List : Berbeda dengan Set, List mampu mengingat urutan. Perilakunya mirip dengan array. Kita dapat mengatur urutan objek yang disimpan di dalamnya. Objek List bisa diurutkan melalui perintah order by dalam SQL, atau dengan adanya nilai index, yang menunjukkan urutan objek di dalam List. Biasanya
List diinisialisasi dengan implementasi ArrayList.

• SortedSet : Interface ini digunakan bila kita ingin menyimpan objek yang tidak boleh sama, tapi menginginkan ada urutan tertentu. Kita dapat memasang Comparator ke dalam SortedSet untuk mengatur urutan penyimpanan objek. Biasanya SortedSet diinisialisasi menggunakan TreeSet.

• Map : Interface ini menyimpan pasangan key dan value. Biasanya diinisialisasi menggunakan HashMap.

Kita memiliki beberapa hubungan many dalam domain model kita. Berikut adalah pemilihan jenis collection beserta pertimbangannya.



Optionality
Masalah optionality ini hanya ada di sisi database. Dari sudut pandang desain object, optionality tidak menjadi pertimbangan.

Mari kita lihat lagi relasi antara User dan Author. Pada kasus kita, satu User pasti memiliki satu Author, dan sebaliknya. Desain seperti ini direalisasikan di database dengan DDL berikut:

create table T_AUTHOR (
id INT PRIMARY KEY AUTO_INCREMENT
);
create table T_USER (
id INT PRIMARY KEY AUTO_INCREMENT,
author_id INT NOT NULL
);


Seperti kita lihat, tabel T_USER di atas memiliki foreign key author_id. Kolom author_id tersebut diberikan atribut NOT NULL karena dia adalah foreign key, sehingga harus diisi.

Desain seperti ini akan menjadi masalah jika business requirement menentukan bahwa User belum tentu punya informasi Author (optional). Jika menggunakan desain di atas, maka kita terpaksa menghilangkan atribut NOT NULL pada author_id.

Cara ini kurang elegan, karena foreign key tidak diperuntukkan agar bisa diisi NULL. Di beberapa merek database, foreign key dilarang NULL. Cara yang lebih baik adalah dengan menggunakan join table. Skema database dengan join table menjadi seperti ini.

create table T_AUTHOR (
id INT PRIMARY KEY AUTO_INCREMENT
);
create table T_USER (
id INT PRIMARY KEY AUTO_INCREMENT
);
create table T_USER_AUTHOR (
user_id INT,
author_id INT,
PRIMARY KEY(user_id, author_id)
);


Kesimpulannya, kita harus mempertimbangkan faktor optionality dalam mendesain struktur tabel database.

Transitive Persistence
Hal lainnya yang harus dipertimbangkan dalam melakukan pemetaan adalah efek suatu operasi entity (save, update, delete) terhadap entity lain yang terhubung dengannya. Misalnya, author Endy menulis artikel baru. Berikut kode program untuk menyimpannya ke database.

Author endy = entityManager.createQuery(“select a from
Article a where a.id=:id”)
.setParameter(“id”,100).getSingleResult();
Article tutorial = new Article();
tutorial.setTitle(“Belajar JPA”);
tutorial.setAuthor(endy);
endy.getArticles().add(tutorial);
entityManager.persist(endy);


Pada kasus di atas, class Article adalah Entity, jadi JPA tidak otomatis melakukan insert. Jika dijalankan, kode di atas akan menghasilkan error, yang menyebutkan bahwa object tutorial belum ada di database, sehingga tidak bisa dihubungkan dengan object endy.

Agar tidak error, kita harus menjalankan save terhadap object tutorial terlebih dulu.

entityManager.persist(tutorial);
entityManager.persist(endy);


Kita dapat menghilangkan perintah save terhadap object tutorial dengan mengaktifkan transitive persistence, yaitu cascadesave. Dengan opsi ini, begitu object endy di-save, JPA akan melakukan save terhadap semua object yang terhubung dengan object endy.

Transitive persistence juga dapat diaplikasikan untuk update dan delete.

Setelah kita memahami pertimbangan dalam membuat mapping, mari sekarang kita buat deklarasi pemetaannya.



Tidak ada komentar:

Posting Komentar