Kegigihan Java dengan JPA dan Hibernate, Bahagian 1: Entiti dan hubungan

Java Persistence API (JPA) adalah spesifikasi Java yang menjembatani jurang antara pangkalan data hubungan dan pengaturcaraan berorientasi objek. Tutorial dua bahagian ini memperkenalkan JPA dan menerangkan bagaimana objek Java dimodelkan sebagai entiti JPA, bagaimana hubungan entiti ditentukan, dan bagaimana menggunakan JPA EntityManagerdengan corak Repository dalam aplikasi Java anda.

Perhatikan bahawa tutorial ini menggunakan Hibernate sebagai penyedia JPA. Sebilangan besar konsep dapat diperluas ke kerangka kerja kegigihan Java yang lain.

Apa itu JPA?

Lihat "Apa itu JPA? Pengenalan Java Persistence API" untuk mempelajari evolusi JPA dan kerangka kerja yang berkaitan, termasuk EJB 3.0. dan JDBC.

Hubungan objek di JPA

Pangkalan data hubungan telah wujud sebagai alat untuk menyimpan data program sejak tahun 1970-an. Walaupun pembangun hari ini mempunyai banyak alternatif untuk pangkalan data hubungan, pangkalan data jenis ini dapat disesuaikan dan difahami dengan baik, dan masih banyak digunakan dalam pengembangan perisian berskala kecil dan besar.

Objek Java dalam konteks pangkalan data relasional didefinisikan sebagai entiti . Entiti diletakkan dalam jadual di mana mereka menempati lajur dan baris. Pengaturcara menggunakan kunci asing dan bergabung dalam jadual untuk menentukan hubungan antara entiti - iaitu hubungan satu-ke-satu, satu-ke-banyak, dan banyak-ke-banyak. Kami juga dapat menggunakan SQL (Structured Query Language) untuk mengambil dan berinteraksi dengan data dalam jadual individu dan merentasi beberapa jadual, menggunakan kekangan kunci asing. Model hubungannya rata, tetapi pembangun dapat menulis pertanyaan untuk mengambil data dan membina objek dari data tersebut.

Ketidakpadanan impedans hubungan-objek

Anda mungkin biasa dengan istilah ketidakcocokan impedansi objek-hubungan , yang merujuk kepada cabaran pemetaan objek data ke pangkalan data relasional. Ketidakcocokan ini berlaku kerana reka bentuk berorientasikan objek tidak terbatas pada hubungan satu-ke-satu, satu-ke-banyak, dan banyak-ke-banyak. Sebaliknya, dalam reka bentuk berorientasikan objek, kita memikirkan objek, sifat dan tingkah laku mereka, dan bagaimana objek berkaitan. Dua contoh adalah enkapsulasi dan pewarisan:

  • Jika objek berisi objek lain, kita menentukannya melalui enkapsulasi - hubungan - hubungan.
  • Jika objek adalah pengkhususan objek yang lain, kami mentakrifkan ini melalui warisan --an adalah-a hubungan.

Gabungan, penggabungan, komposisi, pengabstrakan, generalisasi, realisasi, dan ketergantungan adalah semua konsep pengaturcaraan berorientasikan objek yang boleh mencabar untuk dipetakan ke model relasional.

ORM: Pemetaan hubungan-objek

Ketidaksesuaian antara reka bentuk berorientasi objek dan pemodelan pangkalan data relasional telah menyebabkan kelas alat dikembangkan khusus untuk pemetaan objek-relasional (ORM). Alat ORM seperti Hibernate, EclipseLink, dan iBatis menerjemahkan model pangkalan data hubungan, termasuk entiti dan hubungannya, menjadi model berorientasikan objek. Sebilangan besar alat ini ada sebelum spesifikasi JPA, tetapi tanpa standard ciri mereka bergantung kepada vendor.

Pertama kali diluncurkan sebagai sebahagian dari EJB 3.0 pada tahun 2006, Java Persistence API (JPA) menawarkan cara standard untuk menganotasi objek sehingga dapat dipetakan dan disimpan dalam pangkalan data hubungan. Spesifikasi juga menentukan konstruk umum untuk berinteraksi dengan pangkalan data. Memiliki standard ORM untuk Java membawa konsistensi pelaksanaan vendor, sementara juga memungkinkan untuk fleksibilitas dan tambahan. Sebagai contoh, sementara spesifikasi JPA asli berlaku untuk pangkalan data relasional, beberapa implementasi vendor telah memperpanjang JPA untuk digunakan dengan pangkalan data NoSQL.

Evolusi JPA

Rilis pertama JPA, versi 1.0, diterbitkan pada tahun 2006 melalui Java Community Process (JCP) sebagai Java Specification Request (JSR) 220. Versi 2.0 (JSR 317) diterbitkan pada tahun 2009, versi 2.1 (JSR 338) pada tahun 2013, dan versi 2.2 (keluaran penyelenggaraan JSR 338) diterbitkan pada tahun 2017. JPA 2.2 telah dipilih untuk dimasukkan dan dikembangkan secara berterusan di Jakarta EE.

Bermula dengan JPA

Java Persistence API adalah spesifikasi, bukan implementasi: ini menentukan abstraksi umum yang dapat anda gunakan dalam kod anda untuk berinteraksi dengan produk ORM. Bahagian ini mengulas beberapa bahagian penting dari spesifikasi JPA.

Anda akan belajar bagaimana:

  • Tentukan entiti, bidang, dan kunci utama dalam pangkalan data.
  • Buat hubungan antara entiti dalam pangkalan data.
  • Bekerja dengan kaedah EntityManagerdan kaedahnya.

Mendefinisikan entiti

Untuk menentukan entiti, anda mesti membuat kelas yang diberi penjelasan dengan @Entityanotasi. The @Entityanotasi ialah anotasi penanda , yang digunakan untuk mencari entiti berterusan. Contohnya, jika anda ingin membuat entiti buku, anda akan memberi anotasinya seperti berikut:

 @Entity public class Book { ... } 

Secara lalai, entiti ini akan dipetakan ke Bookjadual, seperti yang ditentukan oleh nama kelas yang diberikan. Sekiranya anda ingin memetakan entiti ini ke jadual lain (dan, secara opsional, skema tertentu), anda boleh menggunakan @Tableanotasi untuk melakukannya. Inilah cara anda memetakan Bookkelas ke jadual BUKU:

 @Entity @Table(name="BOOKS") public class Book { ... } 

Sekiranya jadual BUKU berada dalam skema PUBLISHING, anda boleh menambahkan skema pada @Tableanotasi:

 @Table(name="BOOKS", schema="PUBLISHING") 

Memetakan medan ke lajur

Dengan entiti yang dipetakan ke meja, tugas anda seterusnya adalah menentukan bidangnya. Medan didefinisikan sebagai pemboleh ubah anggota dalam kelas, dengan nama setiap bidang dipetakan ke nama lajur dalam jadual. Anda boleh mengganti pemetaan lalai ini dengan menggunakan @Columnanotasi, seperti yang ditunjukkan di sini:

 @Entity @Table(name="BOOKS") public class Book { private String name; @Column(name="ISBN_NUMBER") private String isbn; ... } 

Dalam contoh ini, kami telah menerima pemetaan lalai untuk nameatribut tetapi menetapkan pemetaan khusus untuk isbnatribut. The nameatribut akan dipetakan kepada nama lajur, tetapi isbnatribut akan dipetakan kepada lajur ISBN_NUMBER itu.

The @Columnanotasi membolehkan kita untuk menentukan ciri-ciri tambahan daripada medan / lajur, termasuk panjang, sama ada ia adalah nol, sama ada ia mestilah unik, ketepatan dan skala (jika ia adalah satu nilai perpuluhan), sama ada ia adalah insertable dan updatable, dan sebagainya .

Menentukan kunci utama

Salah satu syarat untuk jadual pangkalan data hubungan adalah bahawa ia mesti mengandungi kunci utama , atau kunci yang secara unik mengenal pasti baris tertentu dalam pangkalan data. Dalam JPA, kami menggunakan @Idanotasi untuk menetapkan medan menjadi kunci utama jadual. Kunci utama diperlukan untuk menjadi jenis primitif Java, pembungkus primitif, seperti Integeratau Long, a String, a Date, a BigInteger, atau a BigDecimal.

Dalam contoh ini, kami memetakan idatribut, yang merupakan Integer, ke lajur ID dalam jadual BUKU:

 @Entity @Table(name="BOOKS") public class Book { @Id private Integer id; private String name; @Column(name="ISBN_NUMBER") private String isbn; ... } 

Anda juga boleh menggabungkan @Idanotasi dengan @Columnanotasi untuk menimpa pemetaan nama lajur kunci utama.

Hubungan antara entiti

Sekarang setelah anda mengetahui cara menentukan entiti, mari kita lihat bagaimana membuat hubungan antara entiti. JPA menentukan empat anotasi untuk menentukan entiti:

  • @OneToOne
  • @OneToMany
  • @ManyToOne
  • @ManyToMany

Hubungan satu lawan satu

The @OneToOneanotasi digunakan untuk menentukan hubungan satu-ke-satu antara dua entiti. Sebagai contoh, anda mungkin mempunyai Userentiti yang mengandungi nama, e-mel, dan kata laluan pengguna, tetapi anda mungkin ingin menyimpan maklumat tambahan mengenai pengguna (seperti usia, jantina, dan warna kegemaran) di UserProfileentiti yang berasingan . The @OneToOneanotasi memudahkan mogok data dan entiti anda dengan cara ini.

The Userkelas bawah mempunyai satu UserProfilecontoh. The UserProfilemaps kepada satu Usercontoh.

 @Entity public class User { @Id private Integer id; private String email; private String name; private String password; @OneToOne(mappedBy="user") private UserProfile profile; ... } 
 @Entity public class UserProfile { @Id private Integer id; private int age; private String gender; private String favoriteColor; @OneToOne private User user; ... } 

Kegunaan pembekal JPA UserProfile's userlapangan untuk peta UserProfileuntuk User. Pemetaan ditentukan dalam mappedByatribut dalam @OneToOneanotasi.

Hubungan satu-ke-banyak dan banyak-ke-satu

Yang @OneToManydan @ManyToOnepenjelasan memudahkan kedua-dua belah hubungan yang sama. Pertimbangkan contoh di mana kaleng hanya Bookboleh mempunyai satu Author, tetapi Authormungkin mempunyai banyak buku. The Bookentiti akan menentukan @ManyToOnehubungan dengan Authordan Authorentiti akan menentukan @OneToManyhubungan dengan Book.

 @Entity public class Book { @Id private Integer id; private String name; @ManyToOne @JoinColumn(name="AUTHOR_ID") private Author author; ... } 
 @Entity public class Author { @Id @GeneratedValue private Integer id; private String name; @OneToMany(mappedBy = "author") private List books = new ArrayList(); ... } 

Dalam kes ini, Authorkelas menyimpan senarai semua buku yang ditulis oleh pengarang itu dan Bookkelas tersebut menyimpan rujukan kepada pengarangnya yang tunggal. Selain itu, @JoinColumnmenentukan nama lajur dalam Bookjadual untuk menyimpan ID Author.

Hubungan banyak-ke-banyak

Finally, the @ManyToMany annotation facilitates a many-to-many relationship between entities. Here's a case where a Book entity has multiple Authors:

 @Entity public class Book { @Id private Integer id; private String name; @ManyToMany @JoinTable(name="BOOK_AUTHORS", [email protected](name="BOOK_ID"), [email protected](name="AUTHOR_ID")) private Set authors = new HashSet(); ... } 
 @Entity public class Author { @Id @GeneratedValue private Integer id; private String name; @ManyToMany(mappedBy = "author") private Set books = new HashSet(); ... } 

In this example, we create a new table, BOOK_AUTHORS, with two columns: BOOK_ID and AUTHOR_ID. Using the joinColumns and inverseJoinColumns attributes tells your JPA framework how to map these classes in a many-to-many relationship. The @ManyToMany annotation in the Author class references the field in the Book class that manages the relationship; namely the authors property.

That's a quick demo for a fairly complex topic. We'll dive further into the @JoinTable and @JoinColumn annotations in the next article.

Working with the EntityManager

EntityManager is the class that performs database interactions in JPA. It is initialized through a configuration file named persistence.xml. This file is found in the META-INF folder in your CLASSPATH, which is typically packaged in your JAR or WAR file. The persistence.xml file contains:

  • The named "persistence unit," which specifies the persistence framework you're using, such as Hibernate or EclipseLink.
  • A collection of properties specifying how to connect to your database, as well as any customizations in the persistence framework.
  • A list of entity classes in your project.

Let's look at an example.

Configuring the EntityManager

First, we create an EntityManager using the EntityManagerFactory retrieved from the Persistence class:

 EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("Books"); EntityManager entityManager = entityManagerFactory.createEntityManager(); 

In this case we've created an EntityManager that is connected to the "Books" persistence unit, which we've configured in the persistence.xml file.

The EntityManager class defines how our software will interact with the database through JPA entities. Here are some of the methods used by EntityManager:

  • find retrieves an entity by its primary key.
  • createQuery creates a Query instance that can be used to retrieve entities from the database.
  • createNamedQuery loads a Query that has been defined in a @NamedQuery annotation inside one of the persistence entities. Named queries provide a clean mechanism for centralizing JPA queries in the definition of the persistence class on which the query will execute.
  • getTransaction defines an EntityTransaction to use in your database interactions. Just like database transactions, you will typically begin the transaction, perform your operations, and then either commit or rollback your transaction. The getTransaction() method lets you access this behavior at the level of the EntityManager, rather than the database.
  • merge() adds an entity to the persistence context, so that when the transaction is committed, the entity will be persisted to the database. When using merge(), objects are not managed.
  • persist adds an entity to the persistence context, so that when the transaction is committed, the entity will be persisted to the database. When using persist(), objects are managed.
  • refresh refreshes the state of the current entity from the database.
  • flush synchronizes the state of the persistence context with the database.

Jangan bimbang untuk menggabungkan semua kaedah ini sekaligus. Anda akan mengenali mereka dengan bekerja secara langsung dengan EntityManager, yang akan kami lakukan lebih banyak lagi di bahagian seterusnya.