Friday, January 02, 2009

JPA quick notes

(retrieved from Mike Keith and his famous book "Pro EJB3 - Java Persistence API" )


The Java Persistence API (JPA) is the Java standard for mapping Java objects to a relational database. Even though proprietary mapping products like Hibernate and TopLink still exist, they are now focused on providing their functionality through the JPA API, allowing all applications to be portable across JPA implementations. This short article will give users enough to understand the basics of JPA and get started writing JPA applications. It covers entities, identifers, O-R mappings, using an entity manager, creating and executing queries, and confguration of the persistence.xml file.




The basic unit of persistence in JPA is the entity, which is nothing more than a regular Java class with metadata to describe how its state maps to the database tables. Metadata may be in the form of annotations on the entity class itself, or it may be an accompanying XML file, but we are using annotations since they are easier to specify and understand.


Every entity class should have an @Entity marker and an identifier field, indicated by @Id, that is mapped to the primary key column in the database. When a field contains simple data and maps to a regular column in the database we call it a basic mapping, thus an identifier field is a special kind of basic mapping. When an entity has a field that references one or more other entities, that field maps to a foreign key column, and is called a relationship field. Other than the identifier field, basic mappings do not need to be annotated, but relationships must be specified by their relationship cardinality.

Defaulting rules in JPA mean that you are not required to specify table names and column names that an entity is mapped to. If you are not happy with the JPA-assigned defaults then you can always override them through the use of additional mapping
metadata. For example, by putting @Table on the entity class you can make the table name explicit, and by annotating a basic mapping feld with @Column you can defne the particular column that maps the state in that feld. Likewise @JoinColumn is used to override the name of the foreign key column for relationship references.


//Listing 1 - pet entity
@Entity
@Table(name="PET_INFO")
public class Pet {
@Id
@Column(name="ID")
int licenseNumber;
String name;
PetType type;
@ManyToOne
@JoinColumn(name="OWNER_ID")
Owner owner;
//...
}

//Listing 2 - pet owner entity
@Entity
public class Owner {
@Id
int id;
String name;
@Column(name="PHONE_NUM")
String phoneNumber;
@OneToOne
Address address;
@OneToMany(mappedBy="owner")
List pets;
//...
}

In a bidirectional relationship pair, such as the @OneToMany relationship in Owner to Pet and the @ManyToOne relationship back from Pet to Owner, only one foreign key is required in one of the tables to manage both sides of the relationship. As a general rule, the side that does not have the foreign key in it specifes a mappedBy attribute in the relationship annotation and specifes the feld in the related entity that maps the foreign key.




The EntityManager class is the main API in JPA. It is used to create new entities, manufacture queries to return sets of existing entities, merge in the state of remotely modified entities, delete entities from the database, and more.
There are, generally speaking, two main kinds of entity managers:

* container-managed : The managed entity managers may only be obtained within a container that supports the JPA Service Provider Interface (SPI).

* non-managed : Non-managed entity managers may be obtained in any environment where a JPA provider is on the classpath. Listing 3 shows an example of obtaining
a non-managed entity manager by frst obtaining an EntityManagerFactory instance from the Persistence root class.


//Listing 3 – Obtaining a non-managed entity manager
import javax.persistence.*;

//...
EntityManagerFactory emf = Persistence.createEntityManagerFactory("PetShop");
EntityManager em = emf.createEntityManager();
//...
em.close();


In Listing 4 we see how a standard host container can provide a simpler way to obtain an entity manager. The only catch is that this is only supported within standard Java EE components (or containers that are compliant to the JPA container contract), so this example uses a stateless session bean.


//Listing 4 – Injecting a managed entity manager
@Stateless
public class MyBean implements MyInterface {
@PersistenceContext(unitName="PetShop")
EntityManager em;
...
}






The basic purpose of an entity manager is to perform create/
read/update/delete (CRUD) operations on entities. Listing 5
shows methods that perform these operations.


//Listing 5 – Invoking the entity manager
public Pet createPet(int idNum, String name, PetType type) {
Pet pet = new Pet(idNum, name, type);
em.persist(pet);
return pet;
}

public Pet findPet(int id) {
return em.fnd(Pet.class, id);
}

public Pet changeName(int id, String newName) {
Pet pet = this.fndPet(id);
pet.setName(newName);
return pet;
}

public void deletePet(int id) {
Pet pet = this.fndPet(id);
em.remove(pet);
}


Note that fnding the pet is the frst step to being able to perform update and delete operations on it. Also, an update does not even involve invoking the entity manager, but requires reading the pet, loading it into the entity manager and then
modifying it. The modifcation will be refected in the database when the transaction is committed.




Since we just mentioned transactions, but didn’t explain them, now would be a good time to state that JPA supports two different kinds of transactions:

* JTA container transactions : Used when running in container mode

* resource local transactions : Typically used when running in non-container
mode.


JTA transactions are started and committed using the usual container techniques, either calling the UserTransaction API or making use of container-managed transaction demarcation in EJB or Spring. For example, if the methods in Listing 5 were in a session bean that had a Required transaction attribute setting then a transaction would be started at the beginning and committed at the end of each client method invocation.


When using local transactions the transaction must be demarcated manually by invoking on the EntityTransaction instance accessed from the entity manager. Each of the three
methods in Listing 5 that caused the database to change would need to have begin and commit calls, as shown in Listing 6 for the persist method. Methods that only read from the database do not need to occur within a transaction.


//Listing 6 – Using EntityTransaction
public Pet createPet(int idNum, String name, PetType type) {
em.getTransaction().begin();
Pet pet = new Pet(idNum, name, type);
em.persist(pet);
em.getTransaction().commit();
return pet;
}











Dynamic queries are objects that are created from an entity manager, and then executed. The query criteria are specifed at creation time as a Java Persistence Query Language (JPQL) string. Before executing the query a number of possible configuration method calls may be made on the query instance to configure it. Listing 7 shows an example of creating and executing a query that returns all the instances of Pet, or the frst 100 if there are more than 100 instances.


//Listing 7 – Creating and executing a dynamic query
Query q = em.createQuery("SELECT p FROM Pet p");
q.setMaxResults(100);
List results = q.getResultList();


A named query is a query that is defned statically and then
instantiated and executed at runtime. It can be defned as an
annotation on the entity class, and assigned a name that is
used when the query is created. Listing 8 shows a named query
defned on the Pet entity.


//Listing 8 – Defning a named query
@NamedQuery(name="Pet.fndByName",
query="SELECT p FROM Pet p WHERE p.name LIKE :pname")
@Entity
public class Pet {
//...
}


The last identifer is prefxed with a colon (:) character to indicate
that it is a named parameter that must be bound at runtime
before the query can be executed. Listing 9 shows a method
that executes the query by frst instantiating a Query object
using the createNamedQuery() factory method, then binding the pname named parameter to the name that was passed into the method, and fnally executing the query by invoking getResultList().


//Listing 9 – Executing a named query
public List fndAllPetsByName(String petName) {
Query q = em.createNamedQuery("Pet.fndByName");
q.setParameter("pname", petName);
return q.getResultList();
}



Some Query APIs :






The Java Persistence Query Language is SQL-like, but operates over the entities and their mapped persistent attributes instead of the SQL schema. Many of the SQL functions and even reserved words are supported in JP QL.
There are three basic types of JP QL statements, of which the
frst is monstrously the most popular and useful: selects, bulk
updates and bulk deletes.

1. select_clause from_clause [where_clause] [groupby_clause]
[having_clause] [orderby_clause]

2. update_clause [where_clause]

3. delete_clause [where_clause]




Without counting the mappings from the entity to the database tables, there is really only one unit of JPA confguration needed to get your application up and running. It is based on the notion of a persistence unit, and is confgured in a fle called persistence.xml, which must always be placed in the META-INF directory of your deployment unit. Each persistence unit is a confguration closure over the settings necessary to run in the relevant environment. The parent element in a persistence.xml
fle is the persistence element and may contain one or more persistence-unit elements representing different execution confgurations. Each one must be named using the mandatory persistence-unit name attribute.

There are slightly different requirements for configuring the persistence unit, depending upon whether you are deploying to a managed container environment or a non-managed one. In a managed container the target database is indicated through the jta-data-source element, which is the JNDI name for the managed data source describing where the entity state is stored for that confguration unit. In a non-managed environment the target database is typically specified through the use
of vendor-specific properties that describe the JDBC driver and connection properties to use. Also, in non-managed environments the entity classes must be enumerated in class elements, whereas in managed containers the entity classes will be automatically detected. Examples of container and non-container persistence unit elements are indicated in the Listings.




Enjoy,

No comments: