Mulailah dengan Hibernate

Ada baiknya untuk memahami kebutuhan akan pemetaan objek / relasional (ORM) dalam aplikasi Java, tetapi Anda mungkin ingin melihat Hibernate beraksi. Kami akan mulai dengan menunjukkan kepada Anda contoh sederhana yang mendemonstrasikan sebagian dari kekuatannya.

Seperti yang mungkin Anda ketahui, buku pemrograman biasanya dimulai dengan contoh "Hello World". Dalam bab ini, kami mengikuti tradisi itu dengan memperkenalkan Hibernate dengan program "Hello World" yang relatif sederhana. Namun, hanya mencetak pesan ke jendela konsol tidak akan cukup untuk benar-benar mendemonstrasikan mode Hibernasi. Sebagai gantinya, program kami akan menyimpan objek yang baru dibuat dalam database, memperbaruinya, dan melakukan kueri untuk mengambilnya dari database.

Selain contoh kanonik "Hello World", kami memperkenalkan API Hibernate inti dan memberikan detail untuk konfigurasi dasar.

"Hello World" dengan Hibernate

Aplikasi Hibernate mendefinisikan kelas persisten yang "dipetakan" ke tabel database. Contoh "Hello World" kami terdiri dari satu kelas dan satu file pemetaan. Mari kita lihat seperti apa bentuk kelas persisten sederhana, bagaimana pemetaan ditentukan, dan beberapa hal yang dapat kita lakukan dengan instance kelas persisten menggunakan Hibernate.

Tujuan dari aplikasi sampel kami adalah untuk menyimpan pesan dalam database dan mengambilnya untuk ditampilkan. Aplikasi ini memiliki kelas persisten sederhana Message, yang mewakili pesan yang dapat dicetak ini. MessageKelas kami ditunjukkan pada Daftar 1.

Kode 1. Message.java: Kelas persisten sederhana

paket halo; public class Pesan {private Long id; teks String pribadi; Pesan pribadi nextMessage; Pesan pribadi () {} Pesan publik (Teks string) {this.text = teks; } public Long getId () {return id; } private void setId (Id panjang) {this.id = id; } public String getText () {return text; } public void setText (String teks) {this.text = text; } Pesan publik getNextMessage () {return nextMessage; } public void setNextMessage (Pesan nextMessage) {this.nextMessage = nextMessage; }}

MessageKelas kita memiliki tiga atribut: atribut pengenal, teks pesan, dan referensi ke yang lain Message. Atribut pengenal memungkinkan aplikasi untuk mengakses identitas database — nilai kunci utama — dari objek persisten. Jika dua contoh Messagememiliki nilai pengenal yang sama, mereka mewakili baris yang sama dalam database. Kami telah memilih Longjenis atribut pengenal kami, tetapi ini bukan persyaratan. Hibernate memungkinkan hampir semua hal untuk jenis pengenal, seperti yang akan Anda lihat nanti.

Anda mungkin telah memperhatikan bahwa semua atribut Messagekelas memiliki metode pengakses properti gaya JavaBean. Kelas juga memiliki konstruktor tanpa parameter. Kelas persisten yang kita gunakan dalam contoh kita hampir selalu terlihat seperti ini.

Contoh Messagekelas dapat dikelola (dibuat tetap) oleh Hibernate, tetapi tidak harus demikian. Karena Messageobjek tidak mengimplementasikan kelas atau antarmuka khusus Hibernate, kita dapat menggunakannya seperti kelas Java lainnya:

Pesan pesan = Pesan baru ("Hello World"); System.out.println (message.getText ());

Fragmen kode ini melakukan persis seperti yang kita harapkan dari aplikasi "Hello World": Mencetak "Hello World"ke konsol. Mungkin kita terlihat seperti mencoba menjadi manis di sini; Faktanya, kami mendemonstrasikan fitur penting yang membedakan Hibernate dari beberapa solusi persistensi lainnya, seperti biji entitas EJB (Enterprise JavaBean). Kelas persisten kami dapat digunakan dalam konteks eksekusi apa pun — tidak diperlukan wadah khusus. Tentu saja, Anda datang ke sini untuk melihat Hibernate itu sendiri, jadi mari kita simpan yang baru Messageke database:

Sesi sesi = getSessionFactory (). OpenSession (); Transaction tx = session.beginTransaction (); Pesan pesan = Pesan baru ("Hello World"); session.save (pesan); tx.commit (); session.close ();

Kode ini memanggil Hibernate Sessionand Transactioninterfaces. (Kita akan getSessionFactory()segera membahas panggilan itu.) Ini menghasilkan eksekusi yang mirip dengan SQL berikut:

masukkan ke dalam nilai MESSAGES (MESSAGE_ID, MESSAGE_TEXT, NEXT_MESSAGE_ID) (1, 'Hello World', null) 

Tunggu — MESSAGE_IDkolom sedang diinisialisasi ke nilai yang aneh. Kami tidak menetapkan idproperti di messagemana pun, jadi kami mengharapkannya null, bukan? Sebenarnya, idproperti itu spesial: Ini adalah properti pengenal —memegang nilai unik yang dihasilkan. (Kita akan membahas bagaimana nilai dihasilkan nanti.) Nilai ditetapkan ke Messageinstance oleh Hibernate saat save()dipanggil.

Untuk contoh ini, kami berasumsi bahwa MESSAGEStabel tersebut sudah ada. Tentu saja, kami ingin program "Hello World" kami mencetak pesan ke konsol. Sekarang kami memiliki pesan di database, kami siap untuk mendemonstrasikan ini. Contoh berikutnya mengambil semua pesan dari database, dalam urutan abjad, dan mencetaknya:

Sesi newSession = getSessionFactory (). OpenSession (); Transaction newTransaction = newSession.beginTransaction (); Daftar pesan = newSession.find ("dari Pesan sebagai m pesanan oleh m.text asc"); System.out.println (messages.size () + "pesan ditemukan:"); untuk (Iterator iter = messages.iterator (); iter.hasNext ();) {Message message = (Message) iter.next (); System.out.println (message.getText ()); } newTransaction.commit (); newSession.close ();

String literal "from Message as m order by m.text asc"adalah kueri Hibernate, diekspresikan dalam Hibernate Query Language (HQL) berorientasi objek milik Hibernate. Kueri ini secara internal diterjemahkan ke dalam SQL berikut saat find()dipanggil:

pilih m.MESSAGE_ID, m.MESSAGE_TEXT, m.NEXT_MESSAGE_ID dari MESSAGES m pesanan dengan m.MESSAGE_TEXT naik 

Fragmen kode mencetak:

1 pesan ditemukan: Hello World 

Jika Anda belum pernah menggunakan alat ORM seperti Hibernate sebelumnya, Anda mungkin berharap melihat pernyataan SQL di suatu tempat di kode atau metadata. Mereka tidak ada disana. Semua SQL dibuat saat runtime (sebenarnya saat startup, untuk semua pernyataan SQL yang dapat digunakan kembali).

Untuk memungkinkan keajaiban ini terjadi, Hibernate membutuhkan lebih banyak informasi tentang bagaimana Messagekelas harus dibuat persisten. Informasi ini biasanya disediakan dalam dokumen pemetaan XML . Dokumen pemetaan mendefinisikan, antara lain, bagaimana properti Messagekelas dipetakan ke kolom MESSAGEStabel. Mari kita lihat dokumen pemetaan di Listing 2.

Kode 2. Sebuah pemetaan XML Hibernate sederhana


  

Dokumen pemetaan memberi tahu Hibernate bahwa Messagekelas akan dipertahankan ke MESSAGEStabel, bahwa properti pengenal memetakan ke kolom bernama MESSAGE_ID, bahwa properti teks memetakan ke kolom bernama MESSAGE_TEXT, dan bahwa properti bernama nextMessageasosiasi dengan banyak-ke-satu. multiplisitas yang memetakan ke kolom bernama NEXT_MESSAGE_ID. (Jangan khawatirkan detail lainnya untuk saat ini.)

Seperti yang Anda lihat, dokumen XML tidak sulit untuk dipahami. Anda dapat dengan mudah menulis dan memeliharanya dengan tangan. Metode mana pun yang Anda pilih, Hibernate memiliki cukup informasi untuk sepenuhnya menghasilkan semua pernyataan SQL yang akan diperlukan untuk menyisipkan, memperbarui, menghapus, dan mengambil contoh Messagekelas. Anda tidak perlu lagi menulis pernyataan SQL ini dengan tangan.

Catatan
Banyak developer Java yang mengeluhkan "metadata hell" yang menyertai pengembangan J2EE. Beberapa menyarankan perpindahan dari metadata XML kembali ke kode Java biasa. Meskipun kami menghargai saran ini untuk beberapa masalah, ORM mewakili kasus di mana metadata berbasis teks benar-benar diperlukan. Hibernate memiliki default yang masuk akal yang meminimalkan pengetikan dan definisi tipe dokumen dewasa yang dapat digunakan untuk penyelesaian otomatis atau validasi di editor. Anda bahkan dapat secara otomatis menghasilkan metadata dengan berbagai alat.

Sekarang, mari kita ubah pesan pertama kita dan, sementara kita melakukannya, buat pesan baru yang terkait dengan yang pertama, seperti yang ditunjukkan pada Daftar 3.

Kode 3. Memperbarui pesan

Sesi sesi = getSessionFactory (). OpenSession (); Transaction tx = session.beginTransaction (); // 1 adalah id yang dihasilkan dari pesan pertama Message message = (Message) session.load (Message.class, new Long (1)); message.setText ("Greetings Earthling"); Message nextMessage = Pesan baru ("Bawa saya ke pimpinan Anda (tolong)"); message.setNextMessage (nextMessage); tx.commit (); session.close ();

Kode ini memanggil tiga pernyataan SQL di dalam transaksi yang sama:

pilih m.MESSAGE_ID, m.MESSAGE_TEXT, m.NEXT_MESSAGE_ID dari MESSAGES m di mana m.MESSAGE_ID = 1 masukkan ke dalam MESSAGES (MESSAGE_ID, MESSAGE_TEXT, NEXT_MESSAGE_ID) nilai (2, 'Bawa saya ke pemimpin Anda (tolong)', null) perbarui MESSAGES setel MESSAGE_TEXT = 'Greetings Earthling', NEXT_MESSAGE_ID = 2 di mana MESSAGE_ID = 1 

Notice how Hibernate detected the modification to the text and nextMessage properties of the first message and automatically updated the database. We've taken advantage of a Hibernate feature called automatic dirty checking: this feature saves us the effort of explicitly asking Hibernate to update the database when we modify the state of an object inside a transaction. Similarly, you can see that the new message was made persistent when a reference was created from the first message. This feature is called cascading save: it saves us the effort of explicitly making the new object persistent by calling save(), as long as it's reachable by an already persistent instance. Also notice that the ordering of the SQL statements isn't the same as the order in which we set property values. Hibernate uses a sophisticated algorithm to determine an efficient ordering that avoids database foreign key constraint violations but is still sufficiently predictable to the user. This feature is called transactional write-behind.

If we run "Hello World" again, it prints:

2 message(s) found: Greetings Earthling Take me to your leader (please) 

This is as far as we'll take the "Hello World" application. Now that we finally have some code under our belt, we'll take a step back and present an overview of Hibernate's main APIs.

Understanding the architecture

The programming interfaces are the first thing you have to learn about Hibernate in order to use it in the persistence layer of your application. A major objective of API design is to keep the interfaces between software components as narrow as possible. In practice, however, ORM APIs aren't especially small. Don't worry, though; you don't have to understand all the Hibernate interfaces at once. The figure below illustrates the roles of the most important Hibernate interfaces in the business and persistence layers.

We show the business layer above the persistence layer, since the business layer acts as a client of the persistence layer in a traditionally layered application. Note that some simple applications might not cleanly separate business logic from persistence logic; that's okay—it merely simplifies the diagram.

The Hibernate interfaces shown in the figure above may be approximately classified as follows:

  • Interfaces called by applications to perform basic CRUD (create/read/update/delete) and querying operations. These interfaces are the main point of dependency of application business/control logic on Hibernate. They include Session, Transaction, and Query.
  • Interfaces called by application infrastructure code to configure Hibernate, most importantly, the Configuration class.
  • Callback interfaces that allow the application to react to events occurring inside Hibernate, such as Interceptor, Lifecycle, and Validatable.
  • Interfaces that allow extension of Hibernate's powerful mapping functionality, such as UserType, CompositeUserType, and IdentifierGenerator. These interfaces are implemented by application infrastructure code (if necessary).

Hibernate makes use of existing Java APIs, including JDBC (Java Database Connectivity), Java Transaction API (JTA), and Java Naming and Directory Interface (JNDI). JDBC provides a rudimentary level of abstraction of functionality common to relational databases, allowing almost any database with a JDBC driver to be supported by Hibernate. JNDI and JTA allow Hibernate to be integrated with J2EE application servers.

Di bagian ini, kami tidak membahas semantik terperinci metode API Hibernasi, hanya peran dari setiap antarmuka utama. Anda dapat menemukan sebagian besar antarmuka ini di dalam paket net.sf.hibernate. Mari kita lihat sekilas setiap antarmuka secara bergantian.

Antarmuka inti

Lima antarmuka inti digunakan di hampir setiap aplikasi Hibernate. Dengan menggunakan antarmuka ini, Anda dapat menyimpan dan mengambil objek persisten dan mengontrol transaksi.

Antarmuka sesi