CS6320:  SW Engineering of Web Based Systems

 

Google App Engine and Java Persistence API and Java Data Objects

    • JPA (Java Persistence API) and JDO (Java Data Objects) can be used in Google App Engine Web Apps to access the GAE datastore.
    • JPA and JDO implementations in GAE are built on top of low-level API for App Engine datastore 

      JPA and JDO were developed with SQL-based relational databases in mind

      GAE only implements SOME of the standards -- using OpenSource product called DataNucleus Access Platform

       

       

    GAE datastore Concept JPA Concept
    Kind
    Class
    Entity
    Entity (book calls it Data Objects)
    Properties
    Fields

    There are some concepts that are specific only to App Engine and do not have equivalents in JPA

     

You can also use the low-level API that is GAE specific

  • com.google.appengin.api.datastore.* package
  • sometimes this is a necessity to use (as JPA,JDO do not have same functionality)
  • however if you can use JPA or JDO, do so as then your webapp will be more portable (moving to a not GAE web app environment)

see more

 

JPA configuration file in GAE

persistence.xml,

  • location = WAR's WEB-INF/classes/META-INF/

  • Eclipse Development location = can create this file in the src/META-INF/ directory, and Eclipse will copy it to the final location automatically.

<?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>
 



Above Explained

  • use the "appengine" adapter.
  • allow reads and writes outside of transactions (NontransactionalRead and
    NontransactionalWrite are true)
  • named this configuration set "transactions-optional" to match.

Example Application PART I - javax.persistence.EntityManagerFactory to load persistence.xml configuration named specification (see above persistence.xml that has a "transactions-optional" persistence unit defined)

    package myapp; // where "myapp" is your app's package
    import javax.persistence.EntityManagerFactory;
    import javax.persistence.Persistence;
    
    public final class EMF {
    private static final EntityManagerFactory emfInstance = Persistence.createEntityManagerFactory("transactions-optional");
    private EMF() {}
    public static EntityManagerFactory get() {
    return emfInstance;
    }
    }
  • uses an EntityManagerFactory to load the configuration file and uses it for subsequent datastore interactions.
  • The createEntityManagerFactory() static method performs a nontrivial amount of work.
  • An EntityManagerFactory is like a "connection pool", where each "connection" in it will be an instance of EntityManager (see below)
  • EMF class above is simply a Utlity class that has a static vairable for our EntityManagerFactory called emfInstance.


Example Application PART II - using @Entity in plain java class to repesent Data Object to store

  • Using Plain Java Objects makes it easy to test application with out datastore ---make instances of the class

  • @Entity = means this entity will be associated with the Kind of classname = Book

    import java.util.Dateimport java.util.Date;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    @Entity
    public class Book {
    //.....

    }

     

 

  • @Entity(name = "Book") = this entity stored in kind Book

  • @Table(name = "BookItem") = alternative kind name

  • Properties = title,author, copyrightYear, authorBirthdate

  • @Id = key name for Entity (IF PROPERTY IS STRING) OR key id for Entity (when property is long) --in case of key name the application must set this field before saving the object for the first time.

  •  @GeneratedValue(strategy = GenerationType.IDENTITY) = tell JPA to let GAE assign a unique numeric ID instead of using an app provided key name string.

    import java.util.Dateimport java.util.Date;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    @Entity(name = "Book")
    @Table(name = "BookItem")

    public class Book {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String title;
        private String author;
        private int copyrightYear;
        private Date authorBirthdate;
        public Long getId() {
            return id;
        }
        public String getTitle() {
            return title;
        }
        public void setTitle(String title) {
            this.title = title;
        }     public String getAuthor() {
            return author;
        }
        public void setAuthor(String author) {
            this.author = author;
        }
             public int getCopyrightYear() {
            return copyrightYear;
        }
        public void setCopyrightYear(int copyrightYear) {
            this.copyrightYear = copyrightYear;
        }
             public Date getAuthorBirthdate() {
            return authorBirthdate;
        }
             public void setAuthorBirthdate(Date authorBirthdate) {
            this.authorBirthdate = authorBirthdate;
        }
    }

     

    Setting the id to com.google.appengine.api.datastore.Key type -- needed when Entity can have ancestors

 

     import javax.persistence.Entity;
     import javax.persistence.Id;
     import com.google.appengine.api.datastore.Key;
     @Entity(name = "Book")
     public class Book {
        @Id
private Key id;
     // ...
     }

Setting the id to string based Key with directory structure-- ALTERNATIVE when Entity can have ancestors not dependent on google class com.google.appengine.api.datastore.Key

 

import javax.persistence.Entity;
import javax.persistence.Id;
import org.datanucleus.jpa.annotations.Extension;
import com.google.appengine.api.datastore.Key;
@Entity(name = "Book")
public class Book {
@Id
@Extension(vendorName = "datanucleus",
key = "gae.encoded-pk", value = "true")
private String id;

// ...
}

Class variables/fields =

properties of Entity and (in general) will be stored to GAE datastore ---

SOME GAE datatypes must specify should be saved by using @Basic

TO SAY NOT TO SAVE class variables use the @Transient

import java.util.List;
   import javax.persistence.Basic;
   import javax.persistence.Id;
   import javax.persistence.Transient;
   import com.google.appengine.api.datastore.ShortBlob;
   @Entity(name = "Book")
   public class Book {
       // ...
      private String title; // saved
      @Basic // saved
      private ShortBlob coverIcon;

      @Basic // saved
      private List<String> tags;

@Transient // not saved private int debugAccessCount; }

 

OTHER annotations and important ideas

@Column - change the name of the Entity property NOT EQUAL to class variable name

     import javax.persistence.Column;
     import javax.persistence.Entity;
     @Entity(name = "Book")
     public class Book {
           // ...
            @Column(name = "long_description")    //now called long_description NOT longDescription
            private String longDescription;
     }

@Extension = do NOT include this property in Index


import org.datanucleus.jpa.annotations.Extension;
@Entity(name = "Book")
public class Book {
       // ...
      @Extension(vendorName = "datanucleus",
                          key = "gae.unindexed",
                          value = "true")

      private String firstSentence;
}

@Embeddable - declare a class that will be used as the type of an Entity Class's property (class varible)

Fields of embedded classes are stored as separate properties
Can Query them ----e.g. publisher.name

import javax.persistence.Embeddable;
import javax.persistence.Entity;
@Embeddable
public class Publisher {
     private String name;
     private String address;
     private String city;
     private String stateOrProvince;
     private String postalCode;
     // ...
}

@Entity(name = "Book")
public class Book {
     // ...
          private Publisher publisher;       //the Entity property (class variable) publisher is instance of Publisher, the Embeddable class
}

Collection data variables = Collection types are stored as multivalued properties in iteration order.

Multivalued Properties =When loaded into the data class, multivalued properties are converted back into the specified collection type.

Example Application PART IIIa - creating/saving Objects

STEP 1: Create Entity Manager for each Request Handler, ---use the EntityManagerFactory to create an EntityManager.

STEP 2: Use Entity Manager to save, fetch, delete objects

  • CREATION = 1) create instance of an @Entity java class, 2) call EntityManager's persist()

    • If you create an object with a complete key, and an entity with that key already exists
      in the datastore, saving the new object will overwrite the old one.

STEP 3: Close Entity Manager

     import javax.persistence.EntityManager;
     import javax.persistence.EntityManagerFactory;
     import myapp.EMF; // where "myapp" is your app's package
     // ...
     EntityManagerFactory emf = EMF.get();
     EntityManager em = null;
     try {
         em = emf.createEntityManager();     //STEP 1
         

         // ... do datastore stuff ...
     } finally {
        book = new Book();    //CREATE ENTITY
book.title = "The Grapes of Wrath"; //SET POPERTIES
// ask book Entity to be added to datastore via EntityManager
em.persist(book);
em.close(); //STEP 3 }

 

Example Application PART IIIb - fetching Objects

STEP 1: Create Entity Manager for each Request Handler, ---use the EntityManagerFactory to create an EntityManager.

STEP 2: Use Entity Manager to save, fetch, delete objects

  • FETCHING = use EntityManager.find(NameofJavaDataClass.class, "key");

    • 2nd parameter = a string key name, a numeric ID, a datastore.Key object, or a string-encoded complete key

STEP 3: Close Entity Manager


// em is EntityManager Instance
Book book = em.find(Book.class, "9780596156732");   //find Entity associated with Book.java class with key 9780****
if (book == null) {
// not found
}

 

Example Application PART IIIc - updating Objects

STEP 1: Create Entity Manager for each Request Handler, ---use the EntityManagerFactory to create an EntityManager.

STEP 2,3: Use Entity Manager to find the data object you want to update or get passed one from some other part of the code AND then alter its properties

STEP 4: Either call EntityManager.persist(entity_object) or when you Close Entity Manager em.close() it will commite the changes to the datastore.


// em is EntityManager Instance
Book book = em.find(Book.class, "9780596156732");   //find Entity associated with Book.java class with key 9780****
if (book == null) {
// not found
} //change values book.author = "Lynne Grewe"; em.persist(book);

 

Example Application PART IIId - deleting Objects

STEP 1: Create Entity Manager for each Request Handler, ---use the EntityManagerFactory to create an EntityManager.

STEP 2,3: Use Entity Manager to find the data object you want to update

STEP 4: Either call EntityManager.remove(entity_object)


// em is EntityManager Instance
Book book = em.find(Book.class, "9780596156732");   //find Entity associated with Book.java class with key 9780****
if (book == null) {
// not found
} //delete object book em.remove(book);


 

Example Application PART IV - OPTIONALY creating Transaction

Similar to Low-level API for Tansaction

 

You call a method on the entity manager to create a Transaction object, then call methods
on the object to begin and commit or roll back the transaction.

NOTE: The JPA knows nothing of GAE's local transactions and entity groups. Application must group appropriate
operations together

     import javax.persistence.EntityTransaction;
     // ...
     EntityTransaction txn = em.getTransaction();
     txn.begin();
     try {
         Book book = em.find(Book.class, "9780596156732");
         BookReview bookReview = new BookReview();
         bookReview.setRating(5);
         book.getBookReviews().add(bookReview);
         // Persist all updates and commit.
         txn.commit();
     } finally {
         if (txn.isActive()) {
            txn.rollback();
        }
     }
© Lynne Grewe