Dalam artikel yang sangat terlambat ini, saya akan menjelaskan mengapa, menurut pendapat saya, dalam banyak kasus, ketika mengembangkan model data untuk suatu aplikasi, Anda harus mengikuti pendekatan basis data terlebih dahulu. Alih-alih pendekatan “Java [bahasa lain] terlebih dahulu”, yang akan membawa Anda ke jalur panjang penuh dengan rasa sakit dan penderitaan, segera setelah proyek mulai tumbuh.

"Terlalu Sibuk untuk Menjadi Lebih Baik" CC Berlisensi oleh Alan O'Rourke / Audience Stack . Gambar asli
Artikel ini terinspirasi oleh pertanyaan StackOverflow baru-baru ini .
Diskusi reddit yang menarik / r / java dan / r / pemrograman .
Pembuatan Kode
Yang mengejutkan saya, satu kelompok kecil pengguna tampaknya terkejut dengan fakta bahwa jOOQ sangat terkait dengan pembuatan kode sumber.
Meskipun Anda dapat menggunakan jOOQ persis seperti yang Anda suka, cara yang disukai (sesuai dengan dokumentasi) adalah mulai dengan skema database yang ada, kemudian buat kelas klien yang diperlukan (sesuai dengan tabel Anda) menggunakan jOOQ, dan setelah itu mudah untuk menulis jenis-aman pertanyaan untuk tabel ini:
for (Record2<String, String> record : DSL.using(configuration)
Kode dapat dihasilkan secara manual di luar rakitan, atau secara otomatis dengan setiap rakitan. Misalnya, generasi tersebut dapat terjadi segera setelah menginstal migrasi Flyway , yang juga dapat dimulai baik secara manual atau otomatis.
Pembuatan kode sumber
Ada berbagai filosofi, kelebihan, dan kekurangan terkait pendekatan pembuatan kode yang tidak ingin saya bahas dalam artikel ini. Tetapi pada dasarnya, arti dari kode yang dihasilkan adalah bahwa itu adalah representasi Java dari apa yang kita anggap semacam "standar" (baik di dalam maupun di luar sistem kami). Di satu sisi, kompiler melakukan hal yang sama ketika mereka menghasilkan bytecode, kode mesin, atau beberapa kode sumber lain dari sumber - sebagai hasilnya, kami mendapatkan ide tentang "standar" kami dalam bahasa tertentu lainnya.
Ada beberapa generator kode semacam itu. Misalnya, XJC dapat menghasilkan kode Java dari file XSD atau WSDL . Prinsipnya selalu sama:
- Ada beberapa standar (eksternal atau internal), seperti spesifikasi, model data, dll.
- Penting untuk mendapatkan ide Anda sendiri tentang standar ini dalam bahasa pemrograman biasa kami.
Dan hampir selalu masuk akal untuk menghasilkan tampilan ini untuk menghindari pekerjaan yang tidak perlu dan kesalahan yang tidak perlu.
Ketik penyedia dan pemrosesan anotasi
Perlu dicatat bahwa pendekatan lain, lebih modern, untuk pembuatan kode di jOOQ adalah Penyedia Tipe ( seperti yang dilakukan dalam F # ), di mana kode dihasilkan oleh kompiler selama kompilasi dan tidak pernah ada dalam bentuk asli. Alat serupa (tapi kurang canggih) di Jawa adalah prosesor anotasi seperti Lombok .
Dalam kedua kasus, semuanya sama seperti pada pembuatan kode normal, kecuali:
- Anda tidak melihat kode yang dihasilkan (mungkin bagi banyak orang ini merupakan nilai tambah yang besar?)
- Anda harus memastikan bahwa "referensi" Anda tersedia di setiap kompilasi. Ini tidak menimbulkan masalah dalam kasus Lombok, yang secara langsung membubuhi keterangan kode sumber itu sendiri, yang merupakan "standar" dalam kasus ini. Sedikit lebih rumit dengan model database yang mengandalkan koneksi langsung yang selalu aktif.
Apa masalah dengan pembuatan kode?
Selain pertanyaan rumit apakah akan membuat kode secara manual atau otomatis, beberapa orang berpikir bahwa kode tidak perlu dibuat sama sekali. Alasan saya paling sering mendengar adalah bahwa generasi seperti itu sulit diimplementasikan dalam pipa CI / CD. Dan ya, itu benar, karena kami mendapatkan overhead untuk membuat dan mendukung infrastruktur tambahan, terutama jika Anda baru menggunakan alat yang digunakan (jOOQ, JAXB, Hibernate, dll.).
Jika overhead mempelajari generator kode terlalu tinggi, maka akan ada sedikit manfaatnya. Tapi ini satu-satunya argumen yang menentang. Dalam kebanyakan kasus lain, sama sekali tidak masuk akal untuk menulis kode secara manual, yang merupakan representasi biasa dari model sesuatu.
Banyak orang mengklaim bahwa mereka tidak punya waktu untuk ini, karena sekarang Anda perlu meluncurkan MVP lain sesegera mungkin. Dan mereka akan dapat menyelesaikan pipa CI / CD mereka beberapa waktu kemudian. Dalam kasus seperti itu, saya biasanya berkata, "Kamu terlalu sibuk untuk menjadi lebih baik."
"Tapi Hibernate / JPA membuat pengembangan pertama Java jauh lebih mudah."
Ya itu benar. Ini adalah kesenangan dan rasa sakit bagi pengguna Hibernate. Dengannya, Anda cukup menulis beberapa objek dalam bentuk:
@Entity class Book { @Id int id; String title; }
Dan itu hampir selesai. Selanjutnya, Hibernate akan membahas keseluruhan tentang cara mendefinisikan objek ini dalam DDL dan dalam dialek SQL yang diinginkan:
CREATE TABLE book ( id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, title VARCHAR(50), CONSTRAINT pk_book PRIMARY KEY (id) ); CREATE INDEX i_book_title ON book (title);
Ini benar-benar cara yang bagus untuk memulai pengembangan dengan cepat - Anda hanya perlu memulai aplikasi.
Tapi tidak semuanya begitu cerah. Masih banyak pertanyaan:
- Apakah Hibernate akan menghasilkan nama yang saya butuhkan untuk kunci utama?
- Apakah saya akan membuat indeks yang saya butuhkan di bidang TITLE?
- Apakah nilai ID unik akan dihasilkan untuk setiap catatan?
Sepertinya tidak. Tetapi sementara proyek sedang dalam pengembangan, Anda selalu dapat membuang basis data Anda saat ini dan menghasilkan semuanya dari awal dengan menambahkan anotasi yang diperlukan ke model.
Jadi, kelas Buku dalam bentuk akhirnya akan terlihat seperti ini:
@Entity @Table(name = "book", indexes = { @Index(name = "i_book_title", columnList = "title") }) class Book { @Id @GeneratedValue(strategy = IDENTITY) int id; String title; }
Tetapi Anda akan membayar untuk itu, sedikit kemudian
Cepat atau lambat, aplikasi Anda masuk ke produksi, dan skema yang dijelaskan akan berhenti bekerja:
Dalam sistem nyata dan hidup, Anda tidak bisa lagi hanya mengambil dan menjatuhkan basis data Anda, karena data digunakan di dalamnya dan dapat menghabiskan banyak uang.
Mulai sekarang, Anda perlu menulis skrip migrasi untuk setiap perubahan dalam model data, misalnya, menggunakan Flyway . Namun, apa yang terjadi pada kelas klien Anda? Anda dapat mengadaptasinya secara manual (yang akan menghasilkan kerja ganda) atau meminta Hibernate untuk membuatnya (tetapi seberapa besar kemungkinan hasil dari generasi tersebut memenuhi harapan?). Akibatnya, Anda mungkin mengalami masalah besar.
Begitu kode masuk ke produksi, hampir segera diperlukan untuk melakukan koreksi, dan secepat mungkin.
Dan karena instalasi migrasi basis data tidak dibangun ke jalur perakitan Anda, Anda harus menginstal tambalan seperti itu secara manual dengan risiko Anda sendiri. Tidak akan ada cukup waktu untuk kembali dan melakukan semuanya dengan benar. Hanya cukup untuk menyalahkan Hibernate untuk semua masalahnya.
Sebaliknya, Anda bisa bertindak sangat berbeda sejak awal. Yaitu, gunakan roda bundar bukan yang persegi.
Pergi ke Database Pertama
Referensi dan kontrol skema data ada di kantor DBMS Anda. Database adalah satu-satunya tempat di mana skema didefinisikan, dan semua klien memiliki salinan skema itu, tetapi tidak sebaliknya. Data ada di database Anda, dan bukan di klien Anda, jadi masuk akal untuk memberikan kontrol skema dan integritasnya persis di mana data berada.
Ini adalah kebijaksanaan lama, bukan hal baru. Kunci primer dan unik bagus. Kunci asing itu indah. Memeriksa kendala di sisi basis data sangat bagus. Penegasan (ketika mereka akhirnya diimplementasikan) sangat bagus.
Dan itu belum semuanya. Misalnya, jika Anda menggunakan Oracle, Anda dapat menentukan:
- Tablespace di mana tabel Anda?
- Apa arti PCTFREE yang dimilikinya
- Berapa ukuran cache urutan?
Mungkin semua ini tidak masalah pada sistem kecil, tetapi pada sistem yang lebih besar Anda tidak harus mengikuti jalur "data besar" sampai Anda memeras semua jus dari penyimpanan Anda saat ini. Bukan ORM tunggal yang pernah saya lihat (termasuk jOOQ) akan memungkinkan Anda untuk menggunakan set lengkap parameter DDL yang disediakan oleh DBMS Anda. ORM hanya menawarkan beberapa alat untuk membantu Anda menulis DDL.
Pada akhirnya, skema yang dirancang dengan baik hanya boleh ditulis secara manual menggunakan DBL khusus DBMS. Semua DDL yang dibuat secara otomatis hanyalah perkiraan untuk ini.
Bagaimana dengan model klien?
Seperti disebutkan sebelumnya, Anda akan memerlukan representasi tertentu dari skema database di sisi klien. Tidak perlu dikatakan, pandangan ini harus disinkronkan dengan model nyata. Bagaimana cara melakukannya? Tentu saja menggunakan generator kode.
Semua database menyediakan akses ke meta-informasi mereka melalui SQL lama yang baik. Jadi, misalnya, Anda bisa mendapatkan daftar semua tabel dari database yang berbeda:
Ini adalah pertanyaan seperti itu (juga pertanyaan serupa untuk tampilan, tampilan terwujud, dan fungsi tabel) yang dieksekusi ketika metode DatabaseMetaData.getTables () dari driver JDBC tertentu dipanggil, atau dalam modul jOOQ-meta.
Dari hasil pertanyaan seperti itu relatif mudah untuk membuat representasi klien dari model database, terlepas dari teknologi akses data mana yang digunakan.
- Jika Anda menggunakan JDBC atau Spring, Anda bisa membuat grup konstanta String
- Jika menggunakan JPA, Anda dapat membuat objek sendiri
- Jika menggunakan jOOQ, Anda dapat membuat metamodel jOOQ
Bergantung pada jumlah fitur yang ditawarkan data akses API Anda (jOOQ, JPA, atau yang lainnya), metamodel yang dihasilkan dapat benar-benar kaya dan lengkap. Sebagai contoh, fungsi gabungan implisit di jOOQ 3.11, yang bergantung pada meta-informasi tentang hubungan kunci asing antara tabel Anda .
Sekarang, setiap perubahan pada skema database akan secara otomatis mengarah pada pembaruan kode klien.
Bayangkan Anda perlu mengganti nama kolom dalam tabel:
ALTER TABLE book RENAME COLUMN title TO book_title;
Anda yakin ingin melakukan pekerjaan ini dua kali? Tidak mungkin. Cukup komit DDL ini, jalankan build dan nikmati objek yang diperbarui:
@Entity @Table(name = "book", indexes = {
Juga, klien yang diterima tidak perlu dikompilasi setiap waktu (setidaknya sampai perubahan berikutnya dalam skema database), yang sudah bisa menjadi nilai tambah yang besar!
Sebagian besar perubahan DDL juga merupakan perubahan semantik, bukan hanya yang sintaksis. Dengan demikian, sangat bagus untuk melihat dalam kode klien yang dihasilkan apa sebenarnya perubahan terbaru dalam database yang terpengaruh.
Yang benar selalu sendiri
Tidak peduli teknologi apa yang Anda gunakan, harus selalu ada hanya satu model, yang merupakan standar untuk subsistem. Atau, setidaknya, kita harus berusaha untuk ini dan menghindari kebingungan dalam bisnis, di mana "standar" ada di mana-mana dan di mana pun pada saat yang bersamaan. Itu membuat semuanya jauh lebih mudah. Misalnya, jika Anda berbagi file XML dengan beberapa sistem lain, Anda mungkin menggunakan XSD. Sebagai metamodel INFORMATION_SCHEMA jOOQ dalam format XML: https://www.jooq.org/xsd/jooq-meta-3.10.0.xsd
- XSD dipahami dengan baik
- XSD dengan sempurna menggambarkan konten XML dan memungkinkan validasi dalam semua bahasa klien
- XSD membuat versi mudah dan terbelakang kompatibel
- XSD dapat diubah menjadi kode Java menggunakan XJC
Kami memberikan perhatian khusus pada poin terakhir. Saat berkomunikasi dengan sistem eksternal melalui pesan XML, kita harus yakin akan validitas pesan. Dan itu sangat mudah dilakukan dengan hal-hal seperti JAXB, XJC, dan XSD. Akan gila untuk berpikir tentang kesesuaian pendekatan Java-pertama dalam kasus ini. XML yang dihasilkan berdasarkan objek XML akan berkualitas buruk, akan didokumentasikan dengan buruk dan sulit untuk diperluas. Dan jika ada SLA untuk interaksi seperti itu, maka Anda akan kecewa.
Jujur, ini mirip dengan apa yang terjadi dengan berbagai API JSON sekarang, tetapi ini adalah cerita yang sama sekali berbeda ...
Apa yang membuat basis data lebih buruk?
Ketika bekerja dengan database, semuanya sama di sini. Database memiliki data, dan itu juga harus menjadi master dari skema data. Semua modifikasi skema harus dilakukan secara langsung melalui DDL untuk memperbarui referensi.
Setelah memperbarui referensi, semua pelanggan harus memperbarui ide mereka tentang model. Beberapa klien dapat ditulis dalam Java menggunakan jOOQ dan / atau Hibernate atau JDBC. Klien lain dapat ditulis dalam Perl (semoga sukses untuk mereka) atau bahkan dalam C #. Itu tidak masalah. Model utama ada di database. Sementara model yang dibuat menggunakan ORM berkualitas buruk, tidak terdokumentasi dengan baik dan sulit diperluas.
Karena itu, jangan lakukan ini, dan sejak awal pengembangan. Sebaliknya, mulailah dengan basis data. Buat pipa CI / CD otomatis. Gunakan pembuatan kode di dalamnya untuk secara otomatis menghasilkan model database untuk klien untuk setiap build. Dan berhenti khawatir, semuanya akan baik-baik saja. Yang diperlukan hanyalah sedikit usaha awal untuk menyiapkan infrastruktur, tetapi sebagai hasilnya Anda akan mendapatkan keuntungan dalam proses pengembangan untuk sisa proyek Anda di tahun-tahun mendatang.
Tidak terima kasih
Penjelasan
Untuk mengkonsolidasikan: artikel ini sama sekali tidak mengklaim bahwa model database harus berlaku untuk seluruh sistem Anda (area subjek, logika bisnis, dll.). Pernyataan saya hanya terdiri atas fakta bahwa kode klien yang berinteraksi dengan basis data harus hanya merupakan representasi dari skema basis data, tetapi tidak mendefinisikan dan membentuknya dengan cara apa pun.
Dalam arsitektur dua tingkat yang masih memiliki tempat untuk dituju, skema basis data mungkin merupakan satu-satunya sumber informasi tentang model sistem Anda. Namun, pada kebanyakan sistem, saya melihat tingkat akses data sebagai "subsistem" yang merangkum model database. Sesuatu seperti itu.
Pengecualian
Seperti dalam aturan lain yang baik, aturan kami juga memiliki pengecualian (dan saya sudah memperingatkan bahwa pendekatan basis data pertama dan pembuatan kode tidak selalu merupakan pilihan yang tepat). Pengecualian ini (mungkin daftar tidak lengkap):
- Ketika sirkuit tidak diketahui sebelumnya dan perlu diselidiki. Misalnya, Anda adalah penyedia alat untuk membantu pengguna menavigasi skema apa pun. Tentu saja, tidak ada pembuatan kode. Tetapi bagaimanapun juga, Anda harus berurusan dengan database itu sendiri dan skemanya.
- Ketika untuk beberapa tugas Anda perlu membuat skema dengan cepat. Ini mungkin mirip dengan salah satu variasi pola nilai atribut-Entitas , seperti Anda tidak memiliki pola yang jelas. Serta tidak ada kepastian bahwa RDBMS dalam hal ini adalah pilihan yang tepat.
Keunikan dari pengecualian ini adalah bahwa mereka jarang bertemu di alam liar. Dalam kebanyakan kasus, ketika menggunakan basis data relasional, skema diketahui sebelumnya dan merupakan "standar" dari model Anda, dan klien harus bekerja dengan salinan model ini yang dihasilkan menggunakan generator kode.