Memetakan Entitas Tunggal ke Beberapa Tabel di JPA

Ketekunan teratas

Saya baru saja mengumumkan kursus Learn Spring baru , yang berfokus pada dasar-dasar Spring 5 dan Spring Boot 2:

>> LIHAT KURSUSnya

1. Perkenalan

JPA membuat penanganan model database relasional dari aplikasi Java kita tidak terlalu menyakitkan. Semuanya menjadi sederhana ketika kita memetakan setiap tabel ke satu kelas entitas. Namun, terkadang kami memiliki alasan untuk membuat model entitas dan tabel kami secara berbeda:

  • Saat kita ingin membuat grup bidang yang logis, kita dapat memetakan beberapa kelas ke satu tabel
  • Jika pewarisan terlibat, kita bisa memetakan hierarki kelas ke struktur tabel
  • Dalam kasus, ketika bidang terkait tersebar di antara beberapa tabel, dan kami ingin membuat model tabel tersebut dengan satu kelas

Dalam tutorial singkat ini, kita akan melihat bagaimana menangani skenario terakhir ini.

2. Model Data

Katakanlah kita menjalankan sebuah restoran, dan kita ingin menyimpan data tentang setiap makanan yang kita sajikan:

  • nama
  • deskripsi
  • harga
  • jenis alergen apa yang dikandungnya

Karena ada banyak alergen yang mungkin, kami akan mengelompokkan kumpulan data ini menjadi satu. Selanjutnya, kami juga akan memodelkannya menggunakan definisi tabel berikut:

Sekarang mari kita lihat bagaimana kita dapat memetakan tabel ini ke entitas menggunakan anotasi JPA standar.

3. Membuat Banyak Entitas

Solusi paling jelas adalah membuat entitas untuk kedua kelas.

Mari kita mulai dengan mendefinisikan entitas Makanan :

@Entity @Table(name = "meal") class Meal { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") Long id; @Column(name = "name") String name; @Column(name = "description") String description; @Column(name = "price") BigDecimal price; @OneToOne(mappedBy = "meal") Allergens allergens; // standard getters and setters }

Selanjutnya, kami akan menambahkan entitas Alergen :

@Entity @Table(name = "allergens") class Allergens { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "meal_id") Long mealId; @OneToOne @PrimaryKeyJoinColumn(name = "meal_id") Meal meal; @Column(name = "peanuts") boolean peanuts; @Column(name = "celery") boolean celery; @Column(name = "sesame_seeds") boolean sesameSeeds; // standard getters and setters }

Pada contoh di atas, kita dapat melihat bahwa meal_id adalah kunci utama dan juga kunci asing. Itu berarti kita perlu mendefinisikan kolom hubungan satu-ke-satu menggunakan @PrimaryKeyJoinColumn .

Namun, solusi ini memiliki dua masalah:

  • Kami selalu ingin menyimpan alergen untuk makanan, dan solusi ini tidak memberlakukan aturan ini
  • Data makanan dan alergen dimiliki bersama secara logis - oleh karena itu kami mungkin ingin menyimpan informasi ini di kelas Java yang sama meskipun kami membuat beberapa tabel untuk mereka

Salah satu solusi yang mungkin untuk masalah pertama adalah menambahkan anotasi @NotNull ke bidang alergen di entitas Makanan kami . JPA tidak akan membiarkan kami mempertahankan Makan jika kami memiliki Alergen nol .

Namun, ini bukanlah solusi yang ideal; kami menginginkan makanan yang lebih ketat, di mana kami bahkan tidak memiliki kesempatan untuk mencoba mempertahankan Makanan tanpa Alergen.

4. Membuat Entitas Tunggal dengan @SecondaryTable

Kita dapat membuat satu entitas yang menetapkan bahwa kita memiliki kolom di tabel yang berbeda menggunakan anotasi @SecondaryTable :

@Entity @Table(name = "meal") @SecondaryTable(name = "allergens", pkJoinColumns = @PrimaryKeyJoinColumn(name = "meal_id")) class Meal { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") Long id; @Column(name = "name") String name; @Column(name = "description") String description; @Column(name = "price") BigDecimal price; @Column(name = "peanuts", table = "allergens") boolean peanuts; @Column(name = "celery", table = "allergens") boolean celery; @Column(name = "sesame_seeds", table = "allergens") boolean sesameSeeds; // standard getters and setters }

Di balik layar, JPA menggabungkan tabel utama dengan tabel sekunder dan mengisi bidang. Solusi ini mirip dengan hubungan @OneToOne , tetapi dengan cara ini, kita dapat memiliki semua properti di kelas yang sama.

Penting untuk dicatat bahwa jika kita memiliki kolom yang ada di tabel sekunder, kita harus menentukannya dengan argumen tabel dari anotasi @Column . Jika kolom ada di tabel utama, kita dapat menghilangkan argumen tabel karena JPA mencari kolom di tabel utama secara default.

Juga, perhatikan bahwa kita dapat memiliki beberapa tabel sekunder jika kita menyematkannya di @SecondaryTables . Atau, dari Java 8, kita dapat menandai entitas dengan beberapa anotasi @SecondaryTable karena ini adalah anotasi yang dapat diulang.

5. Menggabungkan @SecondaryTable Dengan @Embedded

Seperti yang telah kita lihat, @SecondaryTable memetakan beberapa tabel ke entitas yang sama. Kita juga tahu bahwa @Embedded dan @ Embeddable melakukan hal sebaliknya dan memetakan satu tabel ke beberapa kelas.

Mari kita lihat apa yang kita dapatkan saat menggabungkan @SecondaryTable dengan @Embedded dan @Embeddable :

@Entity @Table(name = "meal") @SecondaryTable(name = "allergens", pkJoinColumns = @PrimaryKeyJoinColumn(name = "meal_id")) class Meal { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") Long id; @Column(name = "name") String name; @Column(name = "description") String description; @Column(name = "price") BigDecimal price; @Embedded Allergens allergens; // standard getters and setters } @Embeddable class Allergens { @Column(name = "peanuts", table = "allergens") boolean peanuts; @Column(name = "celery", table = "allergens") boolean celery; @Column(name = "sesame_seeds", table = "allergens") boolean sesameSeeds; // standard getters and setters }

Ini adalah pendekatan yang mirip dengan apa yang kami lihat menggunakan @OneToOne . Namun, ini memiliki beberapa keunggulan:

  • JPA mengelola dua tabel bersama-sama untuk kita, sehingga kita dapat yakin bahwa akan ada baris untuk setiap makanan di kedua tabel
  • Juga, kodenya sedikit lebih sederhana, karena kita membutuhkan lebih sedikit konfigurasi

Namun demikian, solusi serupa satu-ke-satu ini hanya berfungsi jika kedua tabel memiliki id yang cocok.

Perlu disebutkan bahwa jika kita ingin menggunakan kembali kelas Alergen , akan lebih baik jika kita mendefinisikan kolom dari tabel sekunder di kelas Makanan dengan @AttributeOverride .

6. Kesimpulan

Dalam tutorial singkat ini, kita telah melihat bagaimana kita dapat memetakan beberapa tabel ke entitas yang sama menggunakan anotasi JPA @SecondaryTable .

Kami juga melihat keuntungan menggabungkan @SecondaryTable dengan @Embedded dan @Embeddable untuk mendapatkan hubungan yang mirip satu-ke-satu.

Seperti biasa, contoh tersedia di GitHub.

Ketekunan bawah

Saya baru saja mengumumkan kursus Learn Spring baru , yang berfokus pada dasar-dasar Spring 5 dan Spring Boot 2:

>> LIHAT KURSUSnya