Postingan ini dirancang untuk programmer pemula dan tidak mengandung hal-hal yang sangat rumit.
Saya mengundang semua orang ke hari demo kursus online “Pengembang Java. Profesional " . Sebagai bagian dari acara, saya akan memberi tahu Anda secara rinci tentang program kursus, serta menjawab pertanyaan Anda.
Sebagian besar solusi seperti Hibernate digunakan, karena ini sangat nyaman untuk bekerja dengan objek bersarang.
Misalnya, ada kelas RecordPackage, salah satu bidang dari kelas ini adalah kumpulan objek anak (atau bersarang): catatan.
Jika Anda menggunakan Jdbc, Anda harus menulis banyak kode rutin. Hanya sedikit orang yang menyukainya, itulah sebabnya mereka menggunakan Hiberhate.
Dalam Hibernate, Anda dapat memanggil satu metode sekaligus untuk mendapatkan RecordPackage dengan semua objek rekaman anaknya.
Di satu sisi, saya ingin menggunakan satu metode untuk mendapatkan seluruh objek, dan di sisi lain, saya tidak ingin mengacaukan monster Hibernate.
Spring Data Jdbc memungkinkan Anda mendapatkan yang terbaik dari dua dunia ini (atau setidaknya sesuatu yang dapat diterima).
Pertimbangkan dua kasus:
- hubungan satu ke banyak
- hubungan satu-ke-satu
Koneksi inilah yang paling sering ditemui dalam praktik.
Kode contoh lengkap dapat ditemukan di GitHub, di sini saya hanya akan memberikan yang paling minimum.
Pertama-tama, perlu dicatat bahwa Spring Data Jdbc bukanlah alat ajaib untuk memecahkan masalah apa pun. Ini pasti memiliki kekurangan dan keterbatasan.
Namun, untuk sejumlah tugas umum, ini adalah solusi yang sangat cocok.
Hubungan satu-ke-banyak
Sebagai contoh nyata, Anda dapat mempertimbangkan: header paket beberapa data dan jalur data yang termasuk dalam paket ini. Misalnya, file adalah paket, dan baris file adalah baris data yang masuk ke dalam paket itu.
Struktur tabelnya adalah sebagai berikut:
create table record_package
(
record_package_id bigserial not null
constraint record_package_pk primary key,
name varchar(256) not null
);
create table record
(
record_id bigserial not null
constraint record_pk primary key,
record_package_id bigint not null,
data varchar(256) not null
);
alter table record
add foreign key (record_package_id) references record_package;
dua tabel:
record_package(header dari paket tertentu) dan record(catatan termasuk dalam paket).
Bagaimana hubungan ini ditampilkan dalam kode java:
@Table("record_package")
public class RecordPackage {
@Id
private final Long recordPackageId;
private final String name;
@MappedCollection(idColumn = "record_package_id")
private final Set<Record> records;
….
}
Di sini kami tertarik untuk mendefinisikan hubungan satu-ke-banyak. Ini dikodekan menggunakan anotasi
@MappedCollection.
Anotasi ini memiliki dua parameter:
idColumn - bidang yang digunakan untuk membuat sambungan
; keyColumn - bidang tempat catatan di tabel anak diurutkan.
Urutan ini perlu disebutkan secara terpisah. Dalam contoh ini, tidak penting bagi kami dalam urutan apa rekaman anak akan dimasukkan ke dalam tabel rekaman, tetapi dalam beberapa kasus mungkin penting. Untuk pengurutan seperti itu, tabel record akan memiliki field seperti record_no, field inilah yang perlu dituliskan dalam keyColumn dari anotasi MappedCollection. Saat Anda menjalankan penyisipan, Spring Data Jdbc akan menghasilkan nilai bidang ini. Selain anotasi, Set
<Record >perlu diganti dengan List<Rekam >, yang cukup logis dan bisa dimengerti. Urutan baris turunan yang ditentukan secara eksplisit akan diperhitungkan saat membentuk pemilihan, tapi kita akan kembali ke ini nanti.
Jadi, kami telah mengidentifikasi koneksi dan siap untuk mencobanya.
Kami membuat entitas terkait dan mendapatkannya dari dasar:
var record1 = new Record("r1");
var record2 = new Record("r2");
var record3 = new Record("r3");
var recordPackage = new RecordPackage( "package", Set.of(record1, record2, record3));
var recordPackageSaved = repository.save(recordPackage);
var recordPackageLoaded = repository.findById(recordPackageSaved.getRecordPackageId());
Perhatikan bahwa kita hanya perlu memanggil satu metode
repository.findByIduntuk mendapatkan instance RecordPackagedengan koleksi record yang terisi.
Tentu saja, kami tertarik dengan jenis kueri sql yang dieksekusi untuk mendapatkan koleksi rekaman bersarang.
Dibandingkan dengan Hibernate, Spring Data Jdbc bagus karena kesederhanaannya. Ini dapat dengan mudah di-debug untuk mengungkapkan poin utama.
Setelah sedikit penyelidikan di paket org.springframework.data.jdbc.core.convert kami menemukan kelas DefaultDataAccessStrategy . Kelas ini bertanggung jawab untuk menghasilkan kueri SQL berdasarkan informasi kelas. Sekarang di kelas ini kami tertarik dengan metodenya
Iterable <Object> findAllByPath
Dan lebih tepatnya barisnya:
String findAllByProperty = sql(actualType)
.getFindAllByProperty(identifier, path.getQualifierColumn(), path.isOrdered());
Di sini kueri SQL yang diperlukan diambil dari cache internal.
Dalam kasus kami, ini terlihat seperti ini:
SELECT "record"."data" AS "data", "record"."record_id" AS "record_id", "record"."record_package_id" AS "record_package_id"
FROM "record"
WHERE "record"."record_package_id" = :record_package_id
Semuanya jelas dan dapat diprediksi.
Akan terlihat seperti apa jika kita menggunakan pengurutan record di tabel anak? Tentunya, akan diminta memesan oleh.
Mari beralih ke kelas BasicRelationalPersistentProperty dari paket org.springframework.data.relational.core.mapping. Kelas ini memiliki metode yang menentukan apakah akan menambahkan urutan oleh ke kueri atau tidak.
public boolean isOrdered() {
return isListLike();
}
dan
private boolean isListLike() {
return isCollectionLike() && !Set.class.isAssignableFrom(this.getType());
}
isCollectionLike memverifikasi bahwa kita benar-benar memiliki "koleksi" (termasuk array).
Dan dari kondisi ! Set.class.isAssignableFrom (this.getType ()); menjadi jelas bahwa Set tidak digunakan secara kebetulan, tetapi untuk mengecualikan penyortiran yang tidak perlu. Dan suatu hari nanti kami akan sengaja menggunakan List untuk mengaktifkan pengurutan.
Saya pikir one-to-many kurang lebih jelas, mari kita lanjutkan ke kasus berikutnya.
Hubungan satu-ke-satu
Katakanlah kita memiliki struktur seperti itu.
create table info_main
(
info_main_id bigserial not null
constraint info_pk primary key,
main_data varchar(256) not null
);
create table info_additional
(
info_additional_id bigserial not null
constraint additional_pk primary key,
info_main_id bigint not null,
additional_data varchar(256) not null
);
alter table info_additional
add foreign key (info_main_id) references info_main;
Ada tabel dengan informasi dasar tentang objek tertentu (info_main) dan ada informasi tambahan (info_additional).
Bagaimana ini dapat direpresentasikan dalam kode:
@Table("info_main")
public class InfoMain {
@Id
private final Long infoMainId;
private final String mainData;
@MappedCollection(idColumn = "info_main_id")
private final InfoAdditional infoAdditional;
…
}
Sekilas, ini terlihat seperti kasus satu-ke-banyak yang pertama, tetapi ada perbedaan. Kali ini, anak itu benar-benar sebuah objek, bukan koleksi seperti kasus sebelumnya.
Kode untuk pengujian terlihat seperti ini:
var infoAdditional = new InfoAdditional("InfoAdditional");
var infoMain = new InfoMain("mainData", infoAdditional);
var infoMainSaved = repository.save(infoMain);
var infoMainLoaded = repository.findById(infoMainSaved.getInfoMainId());
Mari kita lihat ekspresi sql mana yang dihasilkan kali ini. Untuk melakukan ini, kita akan menggali metode findById ke lokasi:
Package org.springframework.data.jdbc.core.convert class DefaultDataAccessStrategy . Kita sudah familiar dengan class ini, sekarang kita tertarik dengan metodenya.
public <T> T findById(Object id, Class<T> domainType)
Kami melihat bahwa permintaan berikut diambil dari cache:
SELECT "info_main"."main_data" AS "main_data", "info_main"."info_main_id" AS "info_main_id", "infoAdditional"."info_main_id" AS "infoadditional_info_main_id", "infoAdditional"."additional_data" AS "infoadditional_additional_data", "infoAdditional"."info_additional_id" AS "infoadditional_info_additional_id"
FROM "info_main"
LEFT OUTER JOIN "info_additional" "infoAdditional"
ON "infoAdditional"."info_main_id" = "info_main"."info_main_id"
WHERE "info_main"."info_main_id" = :id
Saat ini, gabungan luar kiri cocok untuk kita, tapi bagaimana jika tidak. Bagaimana cara mendapatkan inner join?
Membuat gabungan fungsional ada dalam paket org.springframework.data.jdbc.core.convert , kelas SqlGenerator , metode:
private SelectBuilder.SelectWhere selectBuilder(Collection<SqlIdentifier> keyColumns)
Kami tertarik dengan fragmen ini:
for (Join join : joinTables) {
baseSelect = baseSelect.leftOuterJoin(join.joinTable).on(join.joinColumn).equals(join.parentId);
}
Jika Anda perlu menggabungkan tabel, maka ada opsi hanya dengan gabungan luar kiri.
Sepertinya inner join belum bisa dilakukan.
Kesimpulan
Kami telah membahas dua kasus paling umum tentang bagaimana Anda dapat menggabungkan tabel di Spring Data Jdbc.
Pada prinsipnya fungsionalitas yang kita miliki saat ini cukup cocok untuk menyelesaikan masalah praktis, walaupun terdapat batasan non kritis.
Teks lengkap dari contoh tersebut dapat ditemukan di sini .
Dan ini adalah versi video dari postingan ini.
