Menggunakan JDO 2.3 dengan App Engine

Objek Data Java (JDO) adalah antarmuka standar untuk mengakses database di Java, yang menyediakan pemetaan antara class Java dan tabel database. Ada plugin open source yang tersedia untuk menggunakan JDO dengan Datastore, dan halaman ini memberikan informasi tentang cara memulainya.

Peringatan: Menurut kami, sebagian besar developer akan memiliki pengalaman yang lebih baik menggunakan Datastore API tingkat rendah, atau salah satu API open source yang dikembangkan khusus untuk Datastore, seperti Objectify. JDO dirancang untuk digunakan dengan database relasional tradisional, sehingga tidak memiliki cara untuk secara eksplisit mewakili beberapa aspek Datastore yang membuatnya berbeda dengan database relasional, seperti entity group dan kueri ancestor. Hal ini dapat menyebabkan masalah ringan yang sulit dipahami dan diperbaiki.

App Engine Java SDK menyertakan implementasi JDO 2.3 untuk App Engine Datastore. Implementasi ini didasarkan pada DataNucleus Access Platform versi 1.0, yang merupakan implementasi referensi open source untuk JDO 2.3.

Catatan: Petunjuk di halaman ini berlaku untuk JDO versi 2.3, yang menggunakan plugin DataNucleus versi 1.0 untuk App Engine. App Engine kini menawarkan plugin DataNucleus 2.x yang memungkinkan Anda menjalankan JDO 3.0. Plugin baru ini mendukung hubungan yang tidak dimiliki serta menyediakan sejumlah API dan fitur baru. Upgrade ini tidak sepenuhnya kompatibel dengan versi sebelumnya. Jika membangun ulang aplikasi menggunakan JDO 3.0, Anda perlu mengupdate dan mengetes ulang kode. Untuk informasi selengkapnya tentang versi baru, lihat JDO 3.0. Untuk mengetahui informasi selengkapnya tentang cara melakukan upgrade, lihat Menggunakan JDO 3.0 dengan App Engine.

Menyiapkan JDO 2.3

Untuk menggunakan JDO guna mengakses datastore, aplikasi App Engine memerlukan hal berikut:

  • JAR plugin JDO dan DataNucleus App Engine harus berada dalam direktori war/WEB-INF/lib/ aplikasi.
  • File konfigurasi bernama jdoconfig.xml harus ada di direktori war/WEB-INF/classes/META-INF/ aplikasi, dengan konfigurasi yang memberi tahu JDO App Engine Datastore.
  • Proses build project harus melakukan langkah "peningkatan" pascakompilasi pada class data yang dikompilasi untuk mengaitkannya dengan implementasi JDO.

Menyalin JAR

JDO dan JAR datastore disertakan dengan App Engine Java SDK. Anda dapat menemukannya di direktori appengine-java-sdk/lib/user/orm/.

Salin JAR ke direktori war/WEB-INF/lib/ aplikasi Anda.

Pastikan appengine-api.jar juga ada dalam direktori war/WEB-INF/lib/. (Anda mungkin sudah menyalin ini saat membuat project.) Plugin App Engine DataNucleus menggunakan JAR ini untuk mengakses datastore.

Membuat File jdoconfig.xml

Antarmuka JDO memerlukan file konfigurasi bernama jdoconfig.xml di direktori war/WEB-INF/classes/META-INF/ aplikasi. Anda dapat membuat file ini di lokasi ini secara langsung, atau meminta proses build menyalin file ini dari direktori sumber.

Buat file dengan konten berikut:

<?xml version="1.0" encoding="utf-8"?>
<jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig">

    <persistence-manager-factory name="transactions-optional">
        <property name="javax.jdo.PersistenceManagerFactoryClass"
            value="org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/>
        <property name="javax.jdo.option.ConnectionURL" value="appengine"/>
        <property name="javax.jdo.option.NontransactionalRead" value="true"/>
        <property name="javax.jdo.option.NontransactionalWrite" value="true"/>
        <property name="javax.jdo.option.RetainValues" value="true"/>
        <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/>
    </persistence-manager-factory>
</jdoconfig>

Menetapkan Kebijakan Baca Datastore dan Batas Waktu Panggilan

Seperti yang dijelaskan di halaman Kueri Datastore, Anda dapat menyesuaikan perilaku Datastore dengan menetapkan kebijakan baca (konsistensi kuat versus konsistensi tertunda) dan batas waktu panggilan. Di JDO, Anda melakukannya dengan menentukan nilai yang diinginkan dalam elemen <persistence-manager-factory> dari file jdoconfig.xml. Semua panggilan yang dilakukan dengan instance PersistenceManager tertentu akan menggunakan nilai konfigurasi yang akan berlaku saat pengelola dibuat oleh PersistenceManagerFactory. Anda juga dapat mengganti setelan ini untuk satu objek Query.

Untuk menetapkan kebijakan baca untuk PersistenceManagerFactory, sertakan properti bernama datanucleus.appengine.datastoreReadConsistency. Nilai yang mungkin adalah EVENTUAL dan STRONG: jika tidak ditentukan, nilai defaultnya adalah STRONG. Perlu diperhatikan bahwa setelan ini hanya berlaku untuk kueri ancestor dalam entity group tertentu. Pada akhirnya, kueri non-ancestor dan lintas grup akan selalu konsisten, terlepas dari kebijakan baca yang berlaku.

        <property name="datanucleus.appengine.datastoreReadConsistency" value="EVENTUAL" />

Anda dapat menetapkan batas waktu panggilan datastore yang terpisah untuk pembacaan dan penulisan. Untuk operasi baca, gunakan properti standar JDO javax.jdo.option.DatastoreReadTimeoutMillis. Untuk operasi tulis, gunakan javax.jdo.option.DatastoreWriteTimeoutMillis. Nilainya adalah jumlah waktu, dalam milidetik.

        <property name="javax.jdo.option.DatastoreReadTimeoutMillis" value="5000" />
        <property name="javax.jdo.option.DatastoreWriteTimeoutMillis" value="10000" />

Jika Anda ingin menggunakan transaksi lintas grup (XG), tambahkan properti berikut:

        <property name="datanucleus.appengine.datastoreEnableXGTransactions" value="true" />

Anda dapat memiliki beberapa elemen <persistence-manager-factory> dalam file jdoconfig.xml yang sama, dengan menggunakan atribut name yang berbeda, untuk menggunakan instance PersistenceManager dengan konfigurasi yang berbeda di aplikasi yang sama. Misalnya, file jdoconfig.xml berikut membuat dua kumpulan konfigurasi, satu bernama "transactions-optional" dan lainnya bernama "eventual-reads-short-deadlines":

<?xml version="1.0" encoding="utf-8"?>
<jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig">

    <persistence-manager-factory name="transactions-optional">
        <property name="javax.jdo.PersistenceManagerFactoryClass"
            value="org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/>
        <property name="javax.jdo.option.ConnectionURL" value="appengine"/>
        <property name="javax.jdo.option.NontransactionalRead" value="true"/>
        <property name="javax.jdo.option.NontransactionalWrite" value="true"/>
        <property name="javax.jdo.option.RetainValues" value="true"/>
        <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/>
    </persistence-manager-factory>

    <persistence-manager-factory name="eventual-reads-short-deadlines">
        <property name="javax.jdo.PersistenceManagerFactoryClass"
            value="org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/>
        <property name="javax.jdo.option.ConnectionURL" value="appengine"/>
        <property name="javax.jdo.option.NontransactionalRead" value="true"/>
        <property name="javax.jdo.option.NontransactionalWrite" value="true"/>
        <property name="javax.jdo.option.RetainValues" value="true"/>
        <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/>

        <property name="datanucleus.appengine.datastoreReadConsistency" value="EVENTUAL" />
        <property name="javax.jdo.option.DatastoreReadTimeoutMillis" value="5000" />
        <property name="javax.jdo.option.DatastoreWriteTimeoutMillis" value="10000" />
    </persistence-manager-factory>
</jdoconfig>

Lihat Mendapatkan Instance PersistenceManager di bawah untuk informasi tentang cara membuat PersistenceManager dengan set konfigurasi bernama.

Meningkatkan Class Data

JDO menggunakan langkah "peningkatan" pascakompilasi dalam proses build untuk mengaitkan class data dengan implementasi JDO.

Anda dapat melakukan langkah peningkatan pada class yang dikompilasi dari command line dengan perintah berikut:

java -cp classpath com.google.appengine.tools.enhancer.Enhance
class-files

classpath harus berisi JAR appengine-tools-api.jar dari direktori appengine-java-sdk/lib/, serta semua class data Anda.

Untuk mengetahui informasi selengkapnya tentang DataNucleus bytecode enhancer, lihat dokumentasi DataNucleus.

Mendapatkan Instance PersistenceManager

Aplikasi berinteraksi dengan JDO menggunakan instance class PersistenceManager. Anda mendapatkan instance ini dengan membuat instance dan memanggil metode pada instance class PersistenceManagerFactory. Factory menggunakan konfigurasi JDO untuk membuat instance PersistenceManager.

Karena instance PersistenceManagerFactory memerlukan waktu untuk diinisialisasi, aplikasi harus menggunakan kembali satu instance. Untuk menerapkan ini, sebuah pengecualian akan dikeluarkan jika aplikasi membuat lebih dari satu instance PersistenceManagerFactory (dengan nama konfigurasi yang sama) Cara mudah untuk mengelola instance PersistenceManagerFactory adalah dengan membuat class wrapper singleton dengan instance statis, sebagai berikut:

PMF.java

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;

public final class PMF {
    private static final PersistenceManagerFactory pmfInstance =
        JDOHelper.getPersistenceManagerFactory("transactions-optional");

    private PMF() {}

    public static PersistenceManagerFactory get() {
        return pmfInstance;
    }
}

Tips: "transactions-optional" merujuk pada nama konfigurasi yang ditetapkan dalam file jdoconfig.xml. Jika aplikasi Anda menggunakan beberapa set konfigurasi, Anda harus memperluas kode ini untuk memanggil JDOHelper.getPersistenceManagerFactory() seperti yang diinginkan. Kode Anda harus meng-cache instance singleton setiap PersistenceManagerFactory.

Aplikasi menggunakan instance factory untuk membuat satu instance PersistenceManager untuk setiap permintaan yang mengakses datastore.

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;

import PMF;

// ...
    PersistenceManager pm = PMF.get().getPersistenceManager();

Anda menggunakan PersistenceManager untuk menyimpan, mengupdate, dan menghapus objek data, serta menjalankan kueri datastore.

Setelah selesai membuat instance PersistenceManager, Anda harus memanggil metode close(). Menggunakan instance PersistenceManager setelah memanggil metode close() merupakan kesalahan.

    try {
        // ... do stuff with pm ...
    } finally {
        pm.close();
    }

Fitur JDO 2.3 yang Tidak Didukung

Fitur antarmuka JDO berikut tidak didukung oleh implementasi App Engine:

  • Hubungan yang tidak dimiliki. Anda dapat menerapkan hubungan yang tidak dimiliki menggunakan nilai Kunci eksplisit. Sintaksis JDO untuk hubungan yang tidak dimiliki mungkin didukung dalam rilis mendatang.
  • Memiliki hubungan many-to-many.
  • Kueri "Gabung". Anda tidak dapat menggunakan kolom entity turunan di filter saat menjalankan kueri pada jenis induk. Perhatikan bahwa Anda dapat menguji kolom hubungan induk secara langsung dalam kueri menggunakan kunci.
  • Pengelompokan JDOQL dan kueri gabungan lainnya.
  • Kueri polimorf. Anda tidak dapat menjalankan kueri class untuk mendapatkan instance subclass. Setiap class diwakili oleh jenis entity terpisah di datastore.
  • IdentityType.DATASTORE untuk anotasi @PersistenceCapable. Hanya mendukung IdentityType.APPLICATION.
  • Saat ini ada bug yang mencegah hubungan one-to-many yang dimiliki apabila induk dan turunan memiliki class yang sama, sehingga sulit untuk membuat model struktur pohon. Hal ini akan diperbaiki dalam rilis mendatang. Anda dapat mengatasi masalah ini dengan menyimpan nilai Kunci eksplisit untuk induk atau turunannya.

Menonaktifkan Transaksi dan Melakukan Porting Aplikasi JDO yang Ada

Konfigurasi JDO yang sebaiknya digunakan menetapkan properti bernama datanucleus.appengine.autoCreateDatastoreTxns ke true. Ini adalah properti khusus App Engine yang memberi tahu implementasi JDO untuk mengaitkan transaksi datastore dengan transaksi JDO yang dikelola dalam kode aplikasi. Jika Anda membangun aplikasi baru dari awal, mungkin inilah yang Anda inginkan. Namun, jika Anda sudah memiliki aplikasi berbasis JDO yang ingin dijalankan di App Engine, sebaiknya gunakan konfigurasi persistensi alternatif yang menetapkan nilai properti ini ke false:

<?xml version="1.0" encoding="utf-8"?>
<jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig">

    <persistence-manager-factory name="transactions-optional">
        <property name="javax.jdo.PersistenceManagerFactoryClass"
            value="org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/>
        <property name="javax.jdo.option.ConnectionURL" value="appengine"/>
        <property name="javax.jdo.option.NontransactionalRead" value="true"/>
        <property name="javax.jdo.option.NontransactionalWrite" value="true"/>
        <property name="javax.jdo.option.RetainValues" value="true"/>
        <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="false"/>
    </persistence-manager-factory>
</jdoconfig>

Untuk memahami alasan kegunaannya, ingat bahwa Anda hanya dapat beroperasi pada objek yang termasuk dalam entity group yang sama dalam sebuah transaksi. Aplikasi yang di-build menggunakan database tradisional biasanya mengasumsikan ketersediaan transaksi global, yang memungkinkan Anda memperbarui kumpulan data apa pun di dalam transaksi. Karena datastore App Engine tidak mendukung transaksi global, App Engine akan menampilkan pengecualian jika kode Anda mengasumsikan ketersediaan transaksi global. Daripada memeriksa codebase (yang berpotensi besar) dan menghapus semua kode pengelolaan transaksi, Anda cukup menonaktifkan transaksi datastore. Ini tidak mengatasi asumsi yang dibuat oleh kode Anda tentang atomicity modifikasi multi-data, tetapi memungkinkan Anda membuat aplikasi berfungsi sehingga Anda dapat berfokus untuk memfaktorkan ulang kode transaksional secara bertahap dan sesuai kebutuhan, bukan sekaligus.