Pertahankan data dengan Objek Data Java, Bagian 1

"Semuanya harus dibuat sesederhana mungkin, tapi tidak lebih sederhana."

Albert Einstein

Kebutuhan untuk mempertahankan data yang dibuat saat runtime sama tuanya dengan komputasi. Dan kebutuhan untuk menyimpan data berorientasi objek terpotong ketika pemrograman berorientasi objek menjadi meluas. Saat ini, sebagian besar aplikasi modern dan nontrivial menggunakan paradigma berorientasi objek untuk memodelkan domain aplikasi. Sebaliknya, pasar database lebih terbagi. Kebanyakan sistem database menggunakan model relasional, tetapi penyimpanan data berbasis objek terbukti sangat diperlukan dalam banyak aplikasi. Selain itu, kami juga memiliki sistem lama yang sering kali perlu kami temui.

Artikel ini mengidentifikasi masalah yang terkait dengan persistensi data di lingkungan middleware transaksional, seperti J2EE (Java 2 Platform, Enterprise Edition), dan menunjukkan bagaimana Java Data Objects (JDO) memecahkan beberapa masalah tersebut. Artikel ini memberikan gambaran umum, bukan tutorial terperinci, dan ditulis dari sudut pandang pengembang aplikasi, bukan perancang implementasi JDO.

Baca seluruh seri tentang Objek Data Java:

  • Bagian 1. Pahami kualitas di balik lapisan ketekunan yang ideal
  • Bagian 2. Sun JDO vs. Castor JDO

Para pengembang, desainer, dan arsitek J2EE Java yang bekerja pada sistem yang harus menyimpan data dalam database relasional atau objek, atau media penyimpanan lainnya harus membaca artikel ini. Saya berasumsi Anda memiliki pengetahuan dasar tentang Java dan beberapa keakraban dengan masalah dan terminologi objek-relasional.

Ketekunan yang transparan: Mengapa repot-repot?

Lebih dari satu dekade upaya berkelanjutan untuk menjembatani runtime berorientasi objek dan ketekunan menunjukkan beberapa pengamatan penting (dicantumkan dalam urutan kepentingan):

  1. Mengabstraksi detail persistensi apa pun dan memiliki API yang bersih, sederhana, dan berorientasi objek untuk melakukan penyimpanan data adalah yang terpenting. Kami tidak ingin menangani detail persistensi dan representasi data internal di penyimpanan data, baik itu relasional, berbasis objek, atau yang lainnya. Mengapa kita harus berurusan dengan konstruksi tingkat rendah dari model penyimpanan data, seperti baris dan kolom, dan terus-menerus menerjemahkannya bolak-balik? Alih-alih, kami perlu berkonsentrasi pada aplikasi kompleks yang harus kami kirimkan kemarin.
  2. Kami ingin menggunakan pendekatan plug-and-play dengan penyimpanan data kami: Kami ingin menggunakan penyedia / implementasi yang berbeda tanpa mengubah satu baris kode sumber aplikasi - dan mungkin tanpa memodifikasi lebih dari beberapa baris dalam file konfigurasi yang sesuai ( s). Dengan kata lain, kita memerlukan standar industri untuk mengakses data berdasarkan objek Java, yang memiliki peran serupa dengan yang dimainkan JDBC (Java Database Connectivity) sebagai standar industri untuk mengakses data berbasis SQL.
  3. Kami ingin menggunakan pendekatan plug-and-play dengan paradigma database yang berbeda - yaitu, kami ingin beralih dari database relasional ke database berorientasi objek dengan sedikit perubahan pada kode aplikasi. Meskipun menyenangkan untuk dimiliki, dalam praktiknya, kemampuan ini seringkali tidak diperlukan.

    Satu komentar di sini: Meskipun database relasional menikmati kehadiran pasar terbesar sejauh ini, menyediakan API persistensi terpadu dan memungkinkan penyedia penyimpanan data untuk bersaing dalam kekuatan implementasi adalah hal yang masuk akal, terlepas dari paradigma yang digunakan penyedia ini. Pendekatan ini pada akhirnya dapat membantu meratakan lapangan bermain antara dua kelompok vendor basis data yang dominan: kamp relasional yang mengakar kuat dan kamp berorientasi objek yang berjuang untuk pangsa pasar.

Tiga penemuan yang tercantum di atas mengarahkan kita untuk menentukan lapisan persistensi, kerangka kerja yang menyediakan API Java tingkat tinggi untuk objek dan hubungan agar dapat bertahan lebih lama dari umur lingkungan runtime (JVM). Kerangka seperti itu harus menampilkan kualitas-kualitas berikut:

  • Kesederhanaan
  • Gangguan minimal
  • Transparansi, yang berarti kerangka kerja menyembunyikan implementasi penyimpanan data
  • API yang konsisten dan ringkas untuk penyimpanan / pengambilan / pembaruan objek
  • Transaction support, artinya kerangka kerja mendefinisikan semantik transaksional yang terkait dengan objek persisten
  • Dukungan untuk lingkungan yang dikelola (misalnya, berbasis server aplikasi) dan juga lingkungan yang tidak dikelola (mandiri)
  • Dukungan untuk ekstra yang diperlukan, seperti cache, kueri, pembuatan kunci utama, dan alat pemetaan
  • Biaya lisensi yang masuk akal - bukan persyaratan teknis, tetapi kita semua tahu bahwa ekonomi yang buruk dapat menghancurkan proyek yang sangat bagus

Saya merinci sebagian besar kualitas di atas di bagian berikut.

Kesederhanaan

Tingkat kesederhanaan tinggi dalam daftar sifat yang saya perlukan untuk kerangka kerja perangkat lunak atau pustaka apa pun (lihat kutipan pembukaan artikel ini). Mengembangkan aplikasi terdistribusi sudah cukup sulit, dan banyak proyek perangkat lunak gagal karena kompleksitas yang buruk (dan, selanjutnya, risiko) manajemen. Sederhana tidak identik dengan simplistik; perangkat lunak harus memiliki semua fitur yang diperlukan yang memungkinkan pengembang untuk melakukan pekerjaannya.

Gangguan minimal

Setiap sistem penyimpanan persisten memasukkan sejumlah gangguan ke dalam kode aplikasi. Lapisan persistensi yang ideal harus meminimalkan intrusi untuk mencapai modularitas yang lebih baik dan, dengan demikian, fungsionalitas plug-and-play.

Untuk tujuan artikel ini, saya mendefinisikan intrusi sebagai:

  • Jumlah kode khusus persistensi yang tersebar di kode aplikasi
  • Kebutuhan untuk mengubah model objek aplikasi Anda dengan harus mengimplementasikan beberapa antarmuka ketekunan - seperti Persistableatau sejenisnya - atau dengan memroses kode yang dihasilkan

Intrusion juga berlaku untuk sistem database berorientasi objek dan, meskipun biasanya lebih sedikit masalah di sana dibandingkan dengan penyimpanan data relasional, ini dapat sangat bervariasi di antara vendor ODBMS (sistem manajemen database berorientasi objek).

Transparansi

Konsep transparansi lapisan persisten cukup sederhana: aplikasi menggunakan API yang sama terlepas dari jenis penyimpanan data (transparansi jenis penyimpanan data), atau vendor penyimpanan data (transparansi vendor penyimpanan data). Transparansi sangat menyederhanakan aplikasi dan meningkatkan pemeliharaannya dengan menyembunyikan detail implementasi penyimpanan data semaksimal mungkin. Secara khusus, untuk penyimpanan data relasional umum, tidak seperti JDBC, Anda tidak perlu membuat pernyataan SQL atau nama kolom secara hardcode, atau mengingat urutan kolom yang dikembalikan oleh kueri. Faktanya, Anda tidak perlu mengetahui SQL atau aljabar relasional, karena keduanya terlalu spesifik untuk implementasi. Transparansi mungkin merupakan sifat terpenting dari lapisan persistensi.

API sederhana dan konsisten

API lapisan persistensi bermuara pada serangkaian operasi yang relatif kecil:

  • Operasi CRUD (buat, baca, perbarui, hapus) dasar pada objek kelas satu
  • Pengelolaan transaksi
  • Manajemen aplikasi dan ketekunan identitas objek
  • Manajemen cache (yaitu, menyegarkan dan mengusir)
  • Pembuatan dan eksekusi kueri

Contoh PersistenceLayerAPI:

public void bertahan (Object obj); // Simpan obj ke penyimpanan data. Beban Objek publik (Kelas c, Objek pK); // Baca obj dengan kunci utama yang diberikan. pembaruan kekosongan publik (Objek obj); // Perbarui objek objek yang dimodifikasi. public void delete (Object obj); // Hapus obj dari database. temuan Koleksi publik (Query q); // Temukan objek yang memenuhi persyaratan kueri kami.

Dukungan transaksi

Lapisan persistensi yang baik membutuhkan beberapa fungsi dasar untuk memulai, menjalankan, atau mengembalikan transaksi. Berikut ini contohnya:

// Transaction (tx) demarcation. public void startTx(); public void commitTx(); public void rollbackTx(); // Choose to make a persistent object transient after all. public void makeTransient(Object o) 

Note: Transaction demarcation APIs are primarily used in nonmanaged environments. In managed environments, the built-in transaction manager often assumes this functionality.

Managed environments support

Managed environments, such as J2EE application servers, have grown popular with developers. Who wants to write middle tiers from scratch these days when we have excellent application servers available? A decent persistence layer should be able to work within any major application server's EJB (Enterprise JavaBean) container and synchronize with its services, such as JNDI (Java Naming and Directory Interface) and transaction management.

Queries

The API should be able to issue arbitrary queries for data searches. It should include a flexible and powerful, but easy-to-use, language -- the API should use Java objects, not SQL tables or other data-store representations as formal query parameters.

Cache management

Cache management can do wonders for application performance. A sound persistence layer should provide full data caching as well as appropriate APIs to set the desired behavior, such as locking levels, eviction policies, lazy loading, and distributed caching support.

Primary key generation

Providing automatic identity generation for data is one of the most common persistence services. Every decent persistence layer should provide identity generation, with support for all major primary key-generation algorithms. Primary key generation is a well-researched issue and numerous primary key algorithms exist.

Mapping, for relational databases only

With relational databases, a data mapping issue arises: the need to translate objects into tables, and to translate relationships, such as dependencies and references, into additional columns or tables. This is a nontrivial problem in itself, especially with complex object models. The topic of object-relational model impedance mismatch reaches beyond this article's scope, but is well publicized. See Resources for more information.

The following list of extras related to mapping and/or relational data stores are not required in the persistence layer, but they make a developer's life much easier:

  • A GUI (graphical user interface) mapping tool
  • Code generators: Autogeneration of DDL (data description language) to create database tables, or autogeneration of Java code and mapping files from DDL
  • Primary key generators: Supporting multiple key-generation algorithms, such as UUID, HIGH-LOW, and SEQUENCE
  • Support for binary large objects (BLOBs) and character-based large objects (CLOBs)
  • Self-referential relations: An object of type Bar referencing another object of type Bar, for example
  • Raw SQL support: Pass-through SQL queries

Example

The following code snippet shows how to use the persistence layer API. Suppose we have the following domain model: A company has one or more locations, and each location has one or more users. The following could be an example application's code:

PersistenceManager pm =PMFactory.initialize(..); Company co = new Company("MyCompany"); Location l1 = new Location1 ("Boston"); Location l2 = new Location("New York"); // Create users. User u1 = new User("Mark"); User u2 = new User("Tom"); User u3 = new User("Mary"); // Add users. A user can only "belong" to one location. L1.addUser(u1); L1.addUser(u2); L2.addUser(u3); // Add locations to the company. co.addLocation(l1); co.addLocation(l2); // And finally, store the whole tree to the database. pm.persist(c); 

In another session, you can look up companies employing the user Tom:

PersistenceManager pm =PMFactory.initialize(...) Collection companiesEmployingToms = pm.find("company.location.user.name = 'Tom'"); 

Untuk penyimpanan data relasional, Anda harus membuat file pemetaan tambahan. Ini mungkin terlihat seperti ini:

    Pengguna Lokasi Perusahaan             

Lapisan persistensi menangani sisanya, yang meliputi berikut ini:

  • Menemukan kelompok objek dependen
  • Mengelola identitas objek aplikasi
  • Mengelola identitas objek yang persisten (kunci utama)
  • Mempertahankan setiap objek dalam urutan yang sesuai
  • Menyediakan manajemen cache
  • Memberikan konteks transaksional yang tepat (kami tidak ingin hanya sebagian dari pohon objek yang bertahan, bukan?)
  • Menyediakan mode penguncian yang dapat dipilih pengguna