Mungkin Anda sudah tahu sesuatu tentang perpustakaan open source Celesta . Jika tidak, itu tidak masalah, sekarang kami akan memberi tahu Anda segalanya. Satu tahun berlalu, versi 7.x dirilis, banyak hal telah berubah, dan sudah waktunya untuk meringkas perubahan, dan pada saat yang sama mengingatkan apa Celesta secara umum.
Jika Anda belum pernah mendengar apa pun tentang Celesta, dan ketika membaca artikel ini Anda ingin tahu untuk tugas bisnis mana penerapannya paling efektif, saya dapat merekomendasikan bagian pertama dari posting lama atau video setengah jam ini (kecuali untuk kata-kata tentang menggunakan bahasa Python). Tapi lebih baik lagi, baca artikel ini dulu. Saya akan mulai dengan perubahan yang terjadi di versi 7, dan kemudian saya akan membahas contoh teknis lengkap menggunakan versi modern Celesta untuk menulis layanan backend kecil untuk aplikasi Java menggunakan Spring Boot.
Apa yang berubah di versi 7.x?
- Kami menolak menggunakan Jython sebagai bahasa bawaan Celesta. Jika sebelumnya kita mulai berbicara tentang Celesta dengan fakta bahwa logika bisnis ditulis dengan Python, sekarang ... bahasa Java apa pun dapat berfungsi sebagai bahasa logika bisnis: Java, Groovy, JRuby, atau Jython yang sama. Sekarang Celesta tidak memanggil kode logika bisnis, tetapi kode logika bisnis menggunakan Celesta dan kelas akses datanya sebagai perpustakaan Java yang paling umum. Ya, kompatibilitas mundur dilanggar karena ini, tetapi ini adalah harga yang kami bayarkan. Sayangnya, taruhan kami pada Jython hilang. Ketika kami mulai menggunakan Jython beberapa tahun yang lalu, itu adalah proyek yang hidup dan menjanjikan, tetapi selama bertahun-tahun perkembangannya melambat, tumpukan dari spesifikasi bahasa terakumulasi, masalah kompatibilitas untuk sebagian besar perpustakaan pip tidak diselesaikan. Sedotan terakhir adalah bug baru dalam rilis bahasa terbaru, yang memanifestasikan diri ketika mengerjakan beban produksi. Kami sendiri tidak memiliki sumber daya untuk mendukung proyek Jython, dan kami memutuskan untuk berpisah dengannya. Celesta tidak lagi bergantung pada Jython.
- Kelas akses data sekarang dihasilkan kode dalam bahasa Java (dan bukan Python, seperti sebelumnya) menggunakan plugin Maven. Dan karena kami beralih dari pengetikan dinamis ke pengetikan statis karena ini, ada lebih banyak peluang untuk refactoring dan menjadi lebih mudah untuk menulis kode yang benar secara subyektif.
- Ekstensi untuk JUnit5 muncul, sehingga menjadi sangat nyaman untuk menulis tes logika yang berfungsi dengan database di JUnit5 (yang akan dibahas nanti).
- Sebuah proyek terpisah telah muncul - spring-boot-starter-celesta , yang seperti namanya, adalah starter Celesta di Spring Boot. Kemampuan untuk mengemas aplikasi Celesta ke dalam layanan Spring Boot yang mudah digunakan dikompensasi dengan hilangnya kemampuan untuk memperbarui aplikasi di server dengan hanya mengubah folder dengan skrip Python.
- Kami mentransfer semua dokumentasi dari Wiki ke format AsciiDoctor , menempatkannya di kontrol versi bersama dengan kode, dan sekarang kami memiliki dokumentasi terbaru untuk setiap rilis Celesta. Untuk rilis terbaru, dokumentasi online tersedia di sini: https://courseorchestra.imtqy.com/celesta/
- Kami sering ditanya apakah mungkin menggunakan migrasi basis data melalui idempoten DDL secara terpisah dari Celesta. Sekarang ada peluang seperti itu menggunakan alat 2bass .
Apa itu Celesta dan apa yang bisa dia lakukan?
Singkatnya, Celesta adalah:
- lapisan menengah antara database relasional dan kode logika bisnis, berdasarkan pada pendekatan desain database-pertama ,
- mekanisme migrasi struktur basis data,
- kerangka kerja untuk menguji kode yang berfungsi dengan data.
Kami mendukung empat jenis database relasional: PostgreSQL, MS SQL Server, Oracle dan H2.
Fitur utama Celesta:
- Prinsip yang sangat mirip dengan prinsip dasar Jawa: "Tulis sekali, jalankan pada setiap RDBMS yang didukung." Kode logika bisnis tidak tahu jenis database apa yang akan dijalankan. Anda dapat menulis kode logika bisnis dan menjalankannya di MS SQL Server, kemudian beralih ke PostgreSQL, dan ini akan terjadi tanpa komplikasi (well, hampir :)
- Restrukturisasi otomatis pada database langsung. Sebagian besar siklus hidup proyek-proyek Celesta terjadi ketika database yang bekerja sudah ada dan diisi dengan data yang perlu disimpan, tetapi juga perlu untuk terus mengubah struktur mereka. Salah satu fitur utama Celesta adalah kemampuan untuk secara otomatis "menyesuaikan" struktur database dengan model data Anda.
- Pengujian. Banyak perhatian diberikan untuk memastikan bahwa kode untuk Celesta dapat diuji, sehingga kami dapat secara otomatis menguji metode yang memodifikasi data dalam database, melakukan ini dengan mudah, cepat dan elegan, tanpa menggunakan alat eksternal seperti DbUnit dan wadah.
Mengapa Anda membutuhkan independensi dari jenis DBMS?
Kemandirian kode logika bisnis dari jenis DBMS bukanlah poin pertama yang kami masukkan: kode yang ditulis untuk Celesta tidak tahu sama sekali dari mana DBMS dijalankan. Mengapa
Pertama, karena fakta bahwa pilihan jenis DBMS bukan masalah teknologi, tetapi masalah politik. Datang ke pelanggan bisnis baru, kita sering menemukan bahwa dia sudah memiliki jenis DBMS favorit di mana dana diinvestasikan, dan pelanggan ingin melihat solusi lain pada infrastruktur yang ada. Lansekap teknologi berubah: PostgreSQL semakin banyak ditemukan di lembaga pemerintah dan perusahaan swasta, meskipun MS SQL Server menang dalam praktik kami beberapa tahun yang lalu. Celesta mendukung DBMS yang paling umum, dan kami tidak khawatir tentang perubahan ini.
Kedua, saya ingin mentransfer kode yang sudah dibuat untuk menyelesaikan masalah standar dari satu proyek ke proyek lain, untuk membuat perpustakaan yang dapat digunakan kembali. Hal-hal seperti direktori hierarki atau modul distribusi notifikasi email pada dasarnya standar, dan mengapa kita perlu mendukung banyak versi untuk pelanggan dengan hubungan yang berbeda?
Ketiga, terakhir namun tidak kalah pentingnya, kemampuan untuk menjalankan tes unit tanpa menggunakan DbUnit dan wadah menggunakan basis data H2 di dalam memori. Dalam mode ini, basis H2 dimulai secara instan. Celesta dengan sangat cepat membuat skema data di dalamnya, setelah itu Anda dapat melakukan tes yang diperlukan dan "melupakan" database. Karena kode logika bisnis benar-benar tidak tahu atas dasar apa itu dieksekusi, maka sesuai, jika ia bekerja tanpa kesalahan pada H2, maka tanpa kesalahan itu akan bekerja pada PostgreSQL. Tentu saja, tugas para pengembang sistem Celesta itu sendiri adalah melakukan semua tes menggunakan DBMS nyata untuk memastikan bahwa platform kami melakukan API yang sama pada hubungan yang berbeda. Dan kami melakukannya. Tetapi pengembang logika bisnis tidak lagi diperlukan.
CelestaSQL
Bagaimana lintas-basementisme tercapai? Tentu saja, dengan biaya bekerja dengan data hanya melalui API khusus yang mengisolasi logika dari setiap basis data spesifik. Celesta menghasilkan kelas Java untuk mengakses data, di satu sisi, dan kode SQL dan beberapa objek tambahan di dalam database, di sisi lain.
Celesta tidak menyediakan pemetaan objek-relasional dalam bentuknya yang paling murni, karena ketika merancang model data, kita tidak berasal dari kelas, tetapi dari struktur basis data. Yaitu, pertama kita membangun model tabel ER, dan kemudian, berdasarkan model ini, Celesta sendiri menghasilkan kelas kursor untuk mengakses data.
Anda dapat mencapai pekerjaan yang sama di semua DBMS yang didukung hanya untuk fungsionalitas yang kira-kira diimplementasikan secara sama di masing-masing. Jika kami secara kondisional menggambarkan serangkaian kemampuan fungsional dari masing-masing pangkalan yang didukung oleh kami dalam bentuk "Lingkaran Euler", maka kami mendapatkan gambar berikut:

Jika kami memberikan kebebasan penuh dari jenis database, maka fungsionalitas yang kami buka untuk programmer logika bisnis harus berada di dalam persimpangan semua pangkalan. Sekilas, tampaknya ini adalah batasan yang signifikan. Ya: beberapa fitur tertentu, misalnya, kami tidak dapat menggunakan SQL Server. Tetapi tanpa kecuali, tabel dukungan database relasional, kunci asing, tampilan, sekuens, kueri SQL dengan BERGABUNG dan GROUP BY. Dengan demikian, kami dapat memberikan peluang ini kepada pengembang. Kami menyediakan pengembang dengan "depersonalized SQL", yang kami sebut "CelestaSQL", dan dalam prosesnya kami menghasilkan permintaan SQL untuk dialek dari database yang sesuai.
Bahasa CelestaSQL mencakup DDL untuk mendefinisikan objek database dan permintaan SELECT untuk tampilan dan filter, tetapi tidak mengandung perintah DML: kursor digunakan untuk memodifikasi data, yang masih harus didiskusikan.
Setiap basis data memiliki jenis data sendiri. CelestaSQL juga memiliki serangkaian tipe sendiri. Pada saat penulisan, ada sembilan dari mereka, dan tabel ini membandingkan mereka dengan tipe nyata di berbagai database dan tipe data Java.
Mungkin terlihat bahwa sembilan jenis tidak cukup (dibandingkan dengan apa yang didukung PostgreSQL, misalnya), tetapi dalam kenyataannya ini adalah jenis yang cukup untuk menyimpan informasi keuangan, perdagangan, dan logistik: string, integer, fraksional , tanggal, nilai boolean, dan gumpalan selalu cukup untuk mewakili data tersebut.
Bahasa CelestaSQL sendiri dijelaskan dalam dokumentasi dengan sejumlah besar diagram sintaks.
Modifikasi struktur basis data. DDL idempoten
Fitur kunci lain dari Celesta adalah pendekatannya untuk memigrasikan struktur database yang berfungsi saat proyek berkembang. Untuk melakukan ini, pendekatan yang dibangun ke dalam Celesta menggunakan idempotent DDL digunakan.
Singkatnya, ketika kita menulis dalam CelestaSQL, teks berikut:
CREATE TABLE OrderLine( order_id VARCHAR(30) NOT NULL, line_no INT NOT NULL, item_id VARCHAR(30) NOT NULL, item_name VARCHAR(100), qty INT NOT NULL DEFAULT 0, cost REAL NOT NULL DEFAULT 0.0, CONSTRAINT Idx_OrderLine PRIMARY KEY (order_id, line_no) );
- teks ini tidak ditafsirkan oleh Celesta sebagai "buat tabel, tetapi jika sudah ada tabel, maka berikan kesalahan", tetapi "bawa tabel ke struktur yang diinginkan". Yaitu: "jika tidak ada tabel, buatlah, jika ada tabel, lihat bidang apa yang ada di dalamnya, dengan tipe apa, yang mengindeks, kunci asing mana, nilai default mana, dll, dan jika ada sesuatu yang perlu diubah di meja ini untuk membawanya ke jenis yang tepat. "
Dengan pendekatan ini, kami menerapkan kemampuan untuk memperbaiki dan mengontrol skrip versi untuk menentukan struktur database:
- kita melihat dalam skrip "gambar yang diinginkan" saat ini dari struktur,
- apa, oleh siapa dan mengapa dalam struktur telah berubah dari waktu ke waktu, kita dapat melihat melalui sistem kontrol versi,
- seperti untuk perintah ALTER, Celesta secara otomatis menghasilkan dan mengeksekusi mereka "di bawah tenda" sebagaimana diperlukan.
Tentu saja, pendekatan ini memiliki keterbatasan. Celesta berusaha keras untuk memastikan bahwa migrasi otomatis tidak menimbulkan rasa sakit dan mulus, tetapi ini tidak mungkin dalam semua kasus. Motivasi, kemungkinan dan keterbatasan pendekatan ini diuraikan dalam posting ini ( versi bahasa Inggrisnya juga tersedia).
Untuk mempercepat proses pengecekan / pembaruan struktur basis data, Celesta menerapkan penyimpanan checksum skrip DDL dalam basis data (sampai checksum diubah, proses pemeriksaan dan pembaruan struktur basis data tidak dimulai). Agar proses pembaruan untuk melanjutkan tanpa masalah yang terkait dengan urutan perubahan objek bergantung satu sama lain, pengurutan topologi dependensi antara skema dengan kunci asing diterapkan. Proses migrasi otomatis dijelaskan secara lebih rinci dalam dokumentasi .
Membuat proyek dan model data Celesta
Proyek demo, yang akan kami pertimbangkan, tersedia di github . Mari kita lihat bagaimana Anda dapat menggunakan Celesta saat menulis aplikasi Spring Boot. Berikut adalah dependensi Maven yang Anda butuhkan:
org.springframework.boot:spring-boot-starter-web
dan ru.curs:spring-boot-starter-celesta
(untuk rincian lebih lanjut, ru.curs:spring-boot-starter-celesta
dokumentasi).- Jika Anda tidak menggunakan Spring Boot, Anda dapat menghubungkan
ru.curs:celesta-system-services
ketergantungan ru.curs:celesta-system-services
secara langsung. - Untuk pembuatan kode kelas akses data berdasarkan skrip Celesta-SQL,
ru.curs:celesta-maven-plugin
diperlukan ru.curs:celesta-maven-plugin
- kode sumber untuk contoh demo atau dokumentasi menjelaskan cara menghubungkannya. - Untuk memanfaatkan kemampuan menulis tes unit JUnit5 untuk metode yang mengubah data, Anda harus menghubungkan
ru.curs:celesta-unit
dalam cakupan pengujian.
Sekarang buat model data dan kompilasi kelas akses data.
Katakanlah kita sedang melakukan proyek untuk perusahaan e-commerce yang baru-baru ini bergabung dengan perusahaan lain. Masing-masing memiliki database sendiri. Mereka mengumpulkan pesanan, tetapi sampai mereka menggabungkan basis data mereka, mereka membutuhkan satu titik masuk untuk mengumpulkan pesanan dari luar.
Implementasi "titik masuk" ini harus sangat tradisional: layanan HTTP dengan operasi CRUD yang menyimpan data dalam basis data relasional.
Karena fakta bahwa Celesta mengimplementasikan pendekatan desain Database-pertama, pertama kita perlu membuat struktur tabel yang menyimpan pesanan. Pesanan, seperti yang Anda ketahui, adalah entitas gabungan: terdiri dari tajuk tempat informasi tentang pelanggan, tanggal pesanan, dan atribut lainnya dari pesanan disimpan, serta banyak baris (item komoditas).
Jadi, untuk pekerjaan itu: buat
src/main/celestasql
- secara default, ini adalah path ke skrip proyek CelestaSQL- ini berisi subfolder yang mengulangi struktur folder paket java (
ru/curs/demo
dalam kasus kami). - di folder paket, buat file
.sql
dengan konten berikut:
CREATE SCHEMA demo VERSION '1.0'; CREATE TABLE OrderHeader( id VARCHAR(30) NOT NULL, date DATETIME, customer_id VARCHAR(30), customer_name VARCHAR(50), manager_id VARCHAR(30), CONSTRAINT Pk_OrderHeader PRIMARY KEY (id) ); CREATE TABLE OrderLine( order_id VARCHAR(30) NOT NULL, line_no INT NOT NULL, item_id VARCHAR(30) NOT NULL, item_name VARCHAR(100), qty INT NOT NULL DEFAULT 0, cost REAL NOT NULL DEFAULT 0.0, CONSTRAINT Idx_OrderLine PRIMARY KEY (order_id, line_no) ); ALTER TABLE OrderLine ADD CONSTRAINT fk_OrderLine FOREIGN KEY (order_id) REFERENCES OrderHeader(id); CREATE VIEW OrderedQty AS SELECT item_id, sum(qty) AS qty FROM OrderLine GROUP BY item_id;
Di sini kami menggambarkan dua tabel yang terhubung dengan kunci asing, dan satu tampilan yang akan mengembalikan jumlah ringkasan untuk barang yang ada di semua pesanan. Seperti yang Anda lihat, ini tidak berbeda dari SQL biasa, dengan pengecualian perintah CREATE SCHEMA
, di mana kami mendeklarasikan versi skema demo
(untuk bagaimana nomor versi memengaruhi migrasi otomatis, lihat dokumentasi ). Tetapi ada juga fitur. Misalnya, semua nama tabel dan bidang yang kami gunakan hanya bisa sedemikian rupa sehingga dapat diubah menjadi nama kelas dan variabel yang valid dalam bahasa Java. Karenanya, spasi, karakter khusus dikecualikan. Anda juga dapat melihat bahwa komentar yang kami letakkan di atas nama tabel dan beberapa bidang, kami tidak memulai dengan / *, seperti biasa, tetapi dengan / **, bagaimana komentar JavaDoc dimulai - dan ini bukan kebetulan! Komentar yang didefinisikan atas entitas yang dimulai dengan / ** akan tersedia saat runtime di properti .getCelestaDoc()
dari entitas itu. Ini berguna ketika kami ingin memberikan elemen-elemen database dengan meta-informasi tambahan: misalnya, nama bidang yang dapat dibaca manusia, informasi tentang cara mewakili bidang dalam antarmuka pengguna, dll.
Skrip CelestaSQL melayani dua tugas yang sama pentingnya: pertama, untuk penyebaran / modifikasi struktur database relasional, dan kedua, untuk pembuatan kode kelas akses data.
Kami dapat membuat kelas akses data sekarang, jalankan saja perintah mvn generate-sources
atau, jika Anda bekerja di IDEA, klik tombol 'Hasilkan sumber dan perbarui folder' di panel kontrol Maven. Dalam kasus kedua, IDEA β target/generated-sources/celesta
folder yang dibuat dalam target/generated-sources/celesta
dan membuat isinya tersedia untuk impor dalam kode sumber proyek. Hasil pembuatan kode akan terlihat sebagai berikut - satu kelas untuk setiap objek dalam database:
Koneksi ke database ditentukan dalam pengaturan aplikasi, dalam kasus kami, dalam file src/main/resources/application.yml
. Saat menggunakan spring-boot-starter-celesta, IDEA akan memberi tahu Anda opsi kode yang tersedia dalam penyelesaian kode.
Jika kami tidak ingin repot dengan RDBMS "nyata" untuk tujuan demonstrasi, kami dapat membuat Celesta bekerja dengan basis data H2 bawaan dalam mode dalam memori menggunakan konfigurasi berikut:
celesta: h2: inMemory: true
Untuk menghubungkan database "nyata", ubah konfigurasi ke sesuatu seperti
celesta: jdbc: url: jdbc:postgresql://127.0.0.1:5432/celesta username: <your_username> password: <your_password>
(dalam hal ini, Anda juga perlu menambahkan driver JDBC PostgreSQL ke aplikasi Anda melalui ketergantungan Maven).
Saat Anda meluncurkan aplikasi Celesta dengan koneksi ke server database, Anda dapat mengamati bahwa tabel, tampilan, indeks, dll. Yang diperlukan dibuat untuk database kosong, dan untuk yang tidak kosong, mereka diperbarui ke struktur yang ditentukan dalam DDL.
Membuat metode manipulasi data
Setelah mengetahui cara membuat struktur basis data, Anda dapat mulai menulis logika bisnis.
Agar dapat menerapkan persyaratan untuk mendistribusikan hak akses dan tindakan logging, setiap operasi pada data di Celesta dilakukan atas nama pengguna, tidak ada operasi "anonim". Oleh karena itu, setiap kode Celesta dieksekusi dalam konteks panggilan yang dijelaskan dalam kelas CallContext .
- Sebelum memulai operasi yang dapat mengubah data dalam database,
CallContext
diaktifkan. - Pada saat aktivasi, koneksi ke database diambil dari kumpulan koneksi dan transaksi dimulai.
- Setelah operasi
CallContext
mengeksekusi commit()
jika operasi berhasil, atau rollback()
jika pengecualian tidak tertangani terjadi selama eksekusi, CallContext
ditutup dan koneksi database dikembalikan ke kumpulan.
Jika kita menggunakan spring-boot-starter-celesta, maka tindakan ini dilakukan secara otomatis untuk semua metode yang dijelaskan oleh @CelestaTransaction
.
Misalkan kita ingin menulis handler yang menyimpan dokumen ke database. Kode level pengontrolnya mungkin terlihat seperti ini:
@RestController @RequestMapping("/api") public class DocumentController { private final DocumentService srv; public DocumentController(DocumentService srv) { this.srv = srv; } @PutMapping("/save") public void saveOrder(@RequestBody OrderDto order) { CallContext ctx = new CallContext("user1");
Sebagai aturan, pada tingkat metode pengontrol (mis., Ketika otentikasi telah berlalu), kami tahu ID pengguna dan dapat menggunakannya saat membuat CallContext
. Mengikat pengguna ke konteks menentukan izin untuk mengakses tabel, dan juga menyediakan kemampuan untuk mencatat perubahan yang dibuat atas namanya. Benar, dalam hal ini, untuk operabilitas kode yang berinteraksi dengan database, hak untuk pengguna "user1" harus ditunjukkan dalam tabel sistem . Jika Anda tidak ingin menggunakan sistem distribusi akses Celesta dan memberikan konteks sesi semua hak ke tabel apa pun, Anda bisa membuat objek SystemCallContext
.
Metode menyimpan faktur di tingkat layanan mungkin terlihat seperti ini:
@Service public class DocumentService { @CelestaTransaction public void postOrder(CallContext context, OrderDto doc) { try (OrderHeaderCursor header = new OrderHeaderCursor(context); OrderLineCursor line = new OrderLineCursor(context)) { header.setId(doc.getId()); header.setDate(Date.from(doc.getDate().atStartOfDay(ZoneId.systemDefault()).toInstant())); header.setCustomer_id(doc.getCustomerId()); header.setCustomer_name(doc.getCustomerName()); header.insert(); int lineNo = 0; for (OrderLineDto docLine : doc.getLines()) { lineNo++; line.setLine_no(lineNo); line.setOrder_id(doc.getId()); line.setItem_id(docLine.getItemId()); line.setQty(docLine.getQty()); line.insert(); } } }
Perhatikan anotasi @CelestaTransaction
. Berkat itu, objek proxy DocumentService
akan melakukan semua tindakan layanan tersebut dengan parameter CallContext ctx
dijelaskan di atas. Artinya, pada awal pelaksanaan metode, itu sudah akan terikat ke koneksi database, dan transaksi akan siap untuk dimulai. Kita dapat fokus pada penulisan logika bisnis. Dalam kasus kami, membaca objek OrderDto
dan menyimpannya ke database.
Untuk melakukan ini, kami menggunakan kursor yang disebut - kelas yang dihasilkan menggunakan celesta-maven-plugin
. Kita sudah melihat apa itu. Satu kelas dibuat untuk masing-masing objek skema - dua tabel dan satu tampilan. Dan sekarang kita bisa menggunakan kelas-kelas ini untuk mengakses objek database dalam logika bisnis kita.
Untuk membuat kursor pada tabel pesanan dan memilih catatan pertama, Anda perlu menulis kode berikut:
OrderHeaderCursor header = new OrderHeaderCursor(context); header.tryFirst();
Setelah membuat objek tajuk, kita dapat mengakses bidang entri tabel melalui getter dan setter:
Saat membuat kursor, kita harus menggunakan konteks panggilan aktif - ini adalah satu-satunya cara untuk membuat kursor. Konteks panggilan membawa informasi tentang pengguna saat ini dan hak aksesnya.
Dengan objek kursor, kita dapat melakukan berbagai hal: menyaring, menelusuri catatan, dan juga, secara alami, menyisipkan, menghapus, dan memperbarui catatan. Seluruh API kursor dijelaskan secara rinci dalam dokumentasi .
Misalnya, kode contoh kita dapat dikembangkan sebagai berikut:
OrderHeaderCursor header = new OrderHeaderCursor(context); header.setRange("manager_id", "manager1"); header.tryFirst(); header.setCounter(header.getCounter() + 1); header.update();
Dalam contoh ini, kami menetapkan filter berdasarkan bidang manager_id, lalu kami menemukan catatan pertama menggunakan metode tryFirst.
(mengapa "mencoba")Metode get
, first
, insert
, update
memiliki dua opsi: tanpa awalan coba (hanya get(...)
, dll.) Dan dengan awalan coba ( tryGet(...)
, tryFirst()
, dll.) . Metode tanpa coba awalan melemparkan pengecualian jika database tidak memiliki data yang sesuai untuk melakukan tindakan. Misalnya, first () akan melempar pengecualian jika tidak ada catatan yang masuk ke filter yang ditetapkan pada kursor. Pada saat yang sama, metode dengan awalan coba tidak membuang pengecualian, tetapi sebaliknya mengembalikan nilai Boolean yang menandakan keberhasilan atau kegagalan operasi yang sesuai. Praktik yang disarankan adalah menggunakan metode tanpa awalan coba sedapat mungkin. Dengan cara ini, kode "swa-uji" dibuat, menandakan kesalahan waktu dalam logika dan / atau data basis data.
Ketika tryFirst
dipicu, variabel tryFirst
diisi dengan data dari satu catatan, kita dapat membaca dan menetapkan nilai kepada mereka. Dan ketika data dalam kursor sepenuhnya siap, kami menjalankan update()
, dan menyimpan isi kursor dalam database.
Masalah apa yang mungkin dipengaruhi oleh kode ini? Tentu saja, munculnya kondisi balapan / pembaruan yang hilang! Karena antara saat ketika kami menerima data sejalan dengan "tryFirst" dan saat ketika kami mencoba memperbarui data ini pada titik "pembaruan", orang lain sudah dapat menerima, memodifikasi, dan memperbarui data ini dalam database. Setelah data dibaca, kursor tidak akan memblokir penggunaannya oleh pengguna lain! Untuk melindungi dari pembaruan yang hilang, Celesta menggunakan prinsip kunci optimis. Di setiap tabel, secara default, Celesta menciptakan bidang recversion
, dan pada tingkat recversion
UPDATE memicu penambahan nomor versi dan memeriksa bahwa data yang diperbarui memiliki versi yang sama dengan tabel. Jika terjadi masalah, lempar pengecualian. Anda dapat membaca lebih lanjut tentang ini di artikel artikel " Perlindungan terhadap pembaruan yang hilang ".
Ingat kembali bahwa transaksi dikaitkan dengan objek CallContext. Jika prosedur Celesta berhasil, komit terjadi. Jika metode Celesta berakhir dengan pengecualian yang tidak ditangani, rollback terjadi. Jadi, jika kesalahan terjadi dalam beberapa prosedur yang rumit, seluruh transaksi yang terkait dengan konteks panggilan dibatalkan, seolah-olah kita belum mulai melakukan apa pun dengan data, data tidak rusak. Jika karena alasan tertentu Anda memerlukan komit di tengah, katakanlah, semacam prosedur besar, maka komit eksplisit dapat dieksekusi dengan memanggil context.commit()
.
Menguji Metode Data
Mari kita buat unit test yang memeriksa kebenaran metode layanan yang menyimpan OrderDto
di database.
Saat menggunakan JUnit5 dan ekstensi untuk JUnit5 tersedia di modul celesta-unit
, ini sangat mudah. Struktur tes adalah sebagai berikut:
@CelestaTest public class DocumentServiceTest { DocumentService srv = new DocumentService(); @Test void documentIsPutToDb(CallContext context) { OrderDto doc =... srv.postOrder(context, doc);
Berkat anotasi @CelestaTest
, yang merupakan ekstensi untuk JUnit5, kami dapat mendeklarasikan parameter CallContext context
dalam metode pengujian. Konteks ini sudah diaktifkan dan terikat ke database (in-memory H2), dan oleh karena itu kami tidak perlu membungkus kelas layanan dalam proxy - kami membuatnya menggunakan yang new
, dan tidak menggunakan Spring. Namun, jika perlu, menyuntikkan layanan ke dalam tes menggunakan alat Spring, tidak ada hambatan untuk ini.
Kami membuat unit test dengan asumsi bahwa pada saat eksekusi mereka database akan benar-benar kosong, tetapi dengan struktur yang kita butuhkan, dan setelah eksekusi mereka kita tidak dapat khawatir tentang fakta bahwa kita meninggalkan "sampah" dalam database. Tes ini dilakukan pada kecepatan yang sangat tinggi.
Mari kita buat prosedur kedua yang mengembalikan JSON dengan nilai agregat yang menunjukkan berapa banyak produk yang kami pesan.
Tes menulis dua pesanan ke database, setelah itu memeriksa nilai total yang dikembalikan oleh metode getAggregateReport
baru:
@Test void reportReturnsAggregatedQuantities(CallContext context) { srv.postOrder(context, . . .); srv.postOrder(context, . . .); Map<String, Integer> result = srv.getAggregateReport(context); assertEquals(5, result.get("A").intValue()); assertEquals(7, result.get("B").intValue()); }
Untuk mengimplementasikan metode getAggregateReport
kita akan menggunakan tampilan OrderedQty, yang saya ingat, dalam file CelestaSQL terlihat seperti ini:
create view OrderedQty as select item_id, sum(qty) as qty from OrderLine group by item_id;
Permintaan standar: kami merangkum garis pesanan berdasarkan jumlah dan kelompok berdasarkan kode produk. Kursor OrderedQtyCursor telah dibuat untuk tampilan, yang dapat kita gunakan. Kami mendeklarasikan kursor ini, beralih di atasnya dan kumpulkan Map<String, Integer>
diinginkan:
@CelestaTransaction public Map<String, Integer> getAggregateReport(CallContext context) { Map<String, Integer> result = new HashMap<>(); try (OrderedQtyCursor ordered_qty = new OrderedQtyCursor(context)) { for (OrderedQtyCursor line : ordered_qty) { result.put(ordered_qty.getItem_id(), ordered_qty.getQty()); } } return result; }
Tampilan Celesta terwujud
Mengapa menggunakan tampilan buruk untuk mendapatkan data agregat? Pendekatan ini cukup bisa diterapkan, tetapi dalam kenyataannya menempatkan bom waktu di bawah seluruh sistem kami: setelah semua, pandangan, yang merupakan permintaan SQL, berjalan lebih lambat dan lebih lambat ketika data terakumulasi dalam sistem. Dia harus meringkas dan mengelompokkan lebih banyak garis. Bagaimana cara menghindarinya?
Celesta mencoba mengimplementasikan semua tugas standar yang selalu dihadapi oleh programmer logika bisnis di tingkat platform.
MS SQL Server memiliki konsep pandangan terwujud (terindeks), yang disimpan sebagai tabel dan diperbarui dengan cepat ketika data dalam tabel sumber berubah. Jika kami bekerja di MS SQL Server "bersih", maka untuk kasus kami, penggantian tampilan dengan tampilan yang diindeks akan sesuai dengan yang kami butuhkan: mengambil laporan teragregasi tidak akan melambat karena data terkumpul, dan pekerjaan untuk memperbarui laporan teragregasi akan dilakukan saat ini memasukkan data ke dalam tabel baris pesanan dan juga tidak akan meningkat banyak dengan peningkatan jumlah baris.
Tetapi jika kita bekerja dengan PostgreSQL melalui Celesta, apa yang bisa kita lakukan? Definisikan kembali tampilan dengan menambahkan kata yang terwujud:
create materialized view OrderedQty as select item_id, sum(qty) as qty from OrderLine group by item_id;
Mari kita mulai sistem dan melihat apa yang terjadi pada basis data.
Kami akan melihat bahwa tampilan OrderedQty
telah menghilang, dan tabel OrderedQty
telah muncul OrderedQty
. Selain itu, karena tabel OrderLine diisi dengan data, informasi dalam tabel OrderedQty akan "diperbarui secara ajaib", seolah-olah OrderedQty akan menjadi tampilan.
Tidak ada keajaiban di sini jika kita melihat pemicu yang dibangun di atas tabel OrderLine
. Celesta, setelah menerima tugas untuk membuat "tampilan terwujud", menganalisis kueri dan membuat pemicu pada tabel OrderLine
yang memperbarui OrderedQty
. Dengan memasukkan satu kata kunci - materialized
- ke dalam file CelestaSQL, kami memecahkan masalah penurunan kinerja, dan kode logika bisnis bahkan tidak perlu diubah!
, , , . «» Celesta , , JOIN-, GROUP BY. , , , , . . .
Celesta. β .