Menyimpan Data Di Google App Engine Dengan JPA


Salah satu hal berbeda yang ditemukan pada saat melakukan hosting aplikasi di App Engine adalah akses database.  Bila cukup beruntung untuk menggunakan layanan berbayar, pengguna dapat memanfaatkan database MySQL yang ada di layanan Google Cloud SQL.  Bila tetap ingin yang gratis, pengguna dapat memakai datastore bawaan App Engine.   Datastore ini bukan dalam bentuk tabel relasional, melainkan sebuah database NoSQL tanpa skema.   Tidak ada tabel!   Tidak ada join!   Berbeda  dengan database relasional, operasi pencarian di datastore sangat cepat.   Object akan disimpan sebagai entity.   Sebuah entity memilih satu atau lebih properties.   Setiap property di entity memiliki nilai sesuai dengan tipe data property tersebut.   Untuk melakukan manipulasi datastore, Google App Engine menyediakan Datastore API.

Bagi yang belum terbiasa, memakai Datastore APIlow-level” akan membutuhkan adaptasi.  Untungnya, datastore App Engine mendukung JPA sehingga kerumitan ini dapat disembunyikan dari programmer.   Artikel ini akan memakai JPA 1.0  di Eclipse yang telah dilengkapi plugin Google App Engine untuk mengakses datastore.

Pada saat membuat proyek baru, Eclipse secara otomatis membuat file src\META-INF\persistence.xml. File yang berisi konfigurasi JPA ini memiliki isi seperti:

<?xml version="1.0" encoding="UTF-8" ?>
  <persistence xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
    http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">

  <persistence-unit name="transactions-optional">
    <provider>org.datanucleus.store.appengine.jpa.DatastorePersistenceProvider</provider>
    <properties>
      <property name="datanucleus.NontransactionalRead" value="true"/>
      <property name="datanucleus.NontransactionalWrite" value="true"/>
      <property name="datanucleus.ConnectionURL" value="appengine"/>
    </properties>
  </persistence-unit>
</persistence>

JPA nantinya akan bertugas menyimpan obyek ke datastore, berdasarkan annotation yang telah ditentukan. Sebagai contoh, berikut ini adalah sebuah class bernama Komentar yang objek-nya bisa disimpan di datastore:

package co.id.jocki.domain;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Komentar {

  @Id
  @GeneratedValue(strategy=GenerationType.IDENTITY)
  private Long id;

  private String user;

  private String pesan;

  public Long getId() {
    return id;
  }

  public void setId(Long id) {
    this.id = id;
  }

  public String getUser() {
    return user;
  }

  public void setUser(String user) {
    this.user = user;
  }

  public String getPesan() {
    return pesan;
  }

  public void setPesan(String pesan) {
    this.pesan = pesan;
  }

}

Atribut id di class Komentar adalah sebuah pengenal yang akan dihasilkan secara unik setiap instance Komentar.  Annotation @GeneratedValue menunjukkan bahwa programmer tidak perlu memberi nilai untuk atribut tersebut karena JPA akan melakukannya secara otomatis.

Berikutnya, untuk memakai JPA, programmer harus mendapatkan sebuah objek EntityManagerFactory. Tetapi karena membuat EntityManagerFactory adalah sesuatu yang berat, maka disarankan untuk memakai instance yang sama di seluruh aplikasi. Hal ini dapat dicapai, misalnya dengan memakai pola singleton, seperti yang diperlihatkan oleh kode program berikut ini:

package co.id.jocki.jpa;

import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public final class LayananJPA {

  private static final EntityManagerFactory emf =
    Persistence.createEntityManagerFactory("transactions-optional");

  private LayananJPA() {}

  public static EntityManagerFactory getEMF() {
    return emf;
  }

}

Untuk memakai JPA,  seseorang bisa membuat sebuah servlet, misalnya LatihanDataServlet. Jangan lupa mendaftarkan class Servlet tersebut ke web.xml dengan menambahkan:

...
<servlet>
  <servlet-name>LatihanDataServlet</servlet-name>
  <servlet-class>co.id.jocki.servlet.LatihanDataServlet</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>LatihanDataServlet</servlet-name>
  <url-pattern>/LatihanDataServlet</url-pattern>
</servlet-mapping>

...

Isi dari LatihanDataServlet dapat berupa:

package co.id.jocki.servlet;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityManager;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import co.id.jocki.domain.Komentar;
import co.id.jocki.jpa.LayananJPA;

public class LatihanDataServlet extends HttpServlet {
  private static final long serialVersionUID = 1L;

  @SuppressWarnings("unchecked")
  protected void doGet(HttpServletRequest request,
    HttpServletResponse response) throws ServletException, IOException {

    EntityManager em = LayananJPA.getEMF().createEntityManager();
    if (request.getParameterMap().containsKey("aksi")) {
      String aksi = request.getParameter("aksi");

      if (aksi.equals("simpan")) {
        // Ambil parameter, hanya periksa seadanya
        // Agar sederhana, tidak ada validasi.
        String user = request.getParameter("user").trim();
        if (user.length() > 50) user = user.substring(0, 50);
        String pesan = request.getParameter("pesan").trim();
        if (pesan.length() > 100) pesan = pesan.substring(0, 100);

        // Buat objek Komentar baru
        Komentar komentar = new Komentar();
        komentar.setUser(user);
        komentar.setPesan(pesan);

        // Simpan ke datastore
        em.getTransaction().begin();
        em.merge(komentar);
        em.getTransaction().commit();

      } else if (aksi.equals("hapus")) {

        // Menghapus objek dari datastore
        em.getTransaction().begin();
        Long id = Long.valueOf(request.getParameter("id"));
        Komentar komentar = em.find(Komentar.class, id);
        if (komentar != null) {
          em.remove(komentar);
        }
        em.getTransaction().commit();

      }
    }

    // Ambil seluruh objek Komentar yang tersimpan di datastore
    em.getTransaction().begin();
    List<Komentar> lstKomentar = em.createQuery("SELECT FROM Komentar k").getResultList();
    em.getTransaction().commit();
    // Duplikasi List untuk dipakai di JSP, karena begitu em di-tutup, data yang ada 
    // di lstKomentar tidak dapat diakses lagi.
    List<Komentar> lstKomentarTemp = new ArrayList<Komentar>(lstKomentar);
    request.setAttribute("lstKomentar", lstKomentarTemp);
    em.close();

    // Tampilkan JSP
    request.getRequestDispatcher("/index.jsp").forward(request, response);
  }

  protected void doPost(HttpServletRequest request,
    HttpServletResponse response) throws ServletException, IOException {
  }
}

Kode program di atas memang sedikit berantakan, tetapi cukup untuk menunjukkan cara mengakses data di datastore App Engine.  Agar lebih rapi, seseorang dapat memisahkan kode program yang memakai entity manager JPA ke sebuah class tersendiri, misalnya KomentarService.   Akan tetapi tujuan artikel ini hanya untuk mendemonstrasikan akses datastore saja.  Tujuannya adalah menunjukkan bahwa dengan memakai JPA di datastore, akses data menjadi gampang karena pembuat program tidak perlu terlibat ke low level API.

Berikutnya, untuk tampilan, dibutuhkan sebuah file JSP seperti berikut ini:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
  <title>Latihan Data</title>
</head>
<body>
  <h3>Data Komentar Yang Tersimpan:</h3>
  <c:forEach var="komentar" items="${lstKomentar}">
    <div style="border: solid 1px black; padding-left: 5px; margin-bottom: 10px; overflow: auto;">
      <p>Nama User: <strong><c:out value="${komentar.user}" escapeXml="true"/></strong></p>
      <p>Pesan: <strong><c:out value="${komentar.pesan}" escapeXml="true"/></strong></p>
      <p><a href="LatihanDataServlet?aksi=hapus&id=${komentar.id}">Hapus</a></p>
    </div>
  </c:forEach>
  <h3>Tambah Komentar:</h3>
  <form action="LatihanDataServlet" method="get">
  <fieldset>
    User: <input type="text" name="user" maxlength="50"/><br/>
    Komentar: <br/>
    <textarea rows="3" cols="50" name="pesan"></textarea><br/>
    <input type="submit" value="Simpan Pesan" />
    <input type="hidden" name="aksi" value="simpan" />
  </fieldset>
  </form>
</body>
</html>

Pada saat mencoba di komputer lokal, Eclipse akan protes karena tidak menemukan library JSTL yang dipakai oleh JSP di atas. Namun, library tersebut memang tidak perlu ditambahkan karena sudah tersedia di server App Engine nanti.

Pada saat mencoba di komputer lokal, kadang-kadang terdapat delay yang aneh antara insert/delete dan query.   Beruntungnya, hal hal tersebut tidak terjadi pada saat di-deploy di server App Engine. Contoh hasil deploy dapat dilihat di http://latihan-java.appspot.com/LatihanDataServlet.

Untuk melihat isi data yang ada di datastore, pembuat aplikasi bisa login ke dalam dashboard Google App Engine dan memilih Datastore Viewer.   Untuk melihat statistik datastore, pengguna bisa memilih Datastore Statistics.

Perihal Solid Snake
I'm nothing...

Apa komentar Anda?

Please log in using one of these methods to post your comment:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s

%d blogger menyukai ini: