Google App Engine and Java Persistence API and Java Data Objects
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
,
-
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.
- javax.persistence.EntityManagerFactory to load persistence.xml configuration named specification (see above persistence.xml that has a "transactions-optional" persistence unit defined)
- 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; } }
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;
// ...
}
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.
- creating/saving Objects
: Create Entity Manager for each Request Handler, ---use the EntityManagerFactory to create an
EntityManager.
: 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.
: 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
}
- fetching Objects
: Create Entity Manager for each Request Handler, ---use the EntityManagerFactory to create an
EntityManager.
: 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
: 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 }
- updating Objects
: Create Entity Manager for each Request Handler, ---use the EntityManagerFactory to create an
EntityManager.
: 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
: 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);
- deleting Objects
: Create Entity Manager for each Request Handler, ---use the EntityManagerFactory to create an
EntityManager.
: Use Entity Manager to find the data object you want to update
: 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);
- 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();
}
}
|