Notes on JPA 2.0

advertisement
Notes on JPA 2.0
Created 10/05/12
Updated 10/26/12, Updated 04/04/14, Updated 06/20/14, Updated 09/03/14, Updated 09/24/14, Updated 10/20/14
Updated 11/12/14
Introduction
This document updates our “Notes on Hibernate” to include how JPA provides a standard interface to persistence.
Concepts
The JPA EntityManager is the conceptual equivalent of the Hibernate Session.
The most useful method on the EntityManager is find(class, id).
Other commonly used methods are save(), merge(), update(), delete().
Create a query, and then call getResultList.
Annotations
Use @Entity and @Table. These can be found from the javax.persistance package and are the same as those in
Hibernate Annotations.
EntityManager Lifecycle
Changes are queued up and then written when the EntityManager is closed.
Lazy Loading
To optimize eager loading by using an outer join you have to add
@Fetch(FetchMode.JOIN)
to your field. This is a Hibernate-specific annotation.
Transaction Management
To be filled in
Referential Integrity Mappings
@ManyToOne
List
@OneToMany
List
@ManyToMany
Set
Using mappedby to avoid having to specify the join column on both sides.
Query Management
JPA has a query builder. Here is an example which returns a list:
Page 1 of 3
public List<Page> findEntityListByVendor(Vendor vendor) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Page> criteria = cb.createQuery(Page.class);
Root<Page> root = criteria.from(Page.class);
Path<Vendor> rootVendor = root.get("vendor");
criteria.where(cb.equal(rootVendor, vendor));
return em.createQuery(criteria).getResultList();
}
Here is another example which returns a single object (or null):
public Page findEntityByName(String name) {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Page> criteria = builder.createQuery(Page.class);
Root<Page> root = criteria.from(Page.class);
Path<String> rootName = root.get("name");
criteria.where(builder.equal(rootName, name));
try {
return em.createQuery(criteria).getSingleResult();
} catch (NoResultException e) {
return null;
}
}
More Complex Query Management
This includes joins, and and/or conditions in the predicateTree used for the where criteria.
public List<Order> findOrdersByEmail(String email) {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Order> criteria = builder.createQuery(Order.class);
Root<Order> root = criteria.from(Order.class);
Join<Order, BillingInfo> orderBillingInfo = root.join("billingInfos");
Join<Order, Donor> orderDonor = root.join("donor");
Predicate predicateTree =
builder.or(
builder.equal(orderBillingInfo.get("billingEmail"), email),
builder.equal(orderDonor.get("login"), email));
criteria.where(predicateTree);
criteria.orderBy(builder.asc(root.get("dateCreated")));
return orders = em.createQuery(criteria).getResultList();
}
Query Language
"node to traverse cannot be null!" is a generic Hibernate error message indicating a syntax problem in your query.
As another example, forgetting to start a select clause with the word "SELECT" would yield the same error.
In this instance the syntax error is due to the on clause - HQL does not support them. Instead do a cross join like
so:
FROM track_history_items thi, artists art
WHERE thi.type = "TrackBroadcast"
AND thi.artist_id = art.id
GROUP BY art.name
ORDER thi.createdAt DESC
Page 2 of 3
Native Queries
It's interesting to note that you're not limited to JPQL when defining queries to be then executed with Query API.
You may be surprised to learn that the EntityManager API offers methods for creating instances of Query for
executing native SQL statements. The most important thing to understand about native SQL queries created with
EntityManager methods is that they, like JPQL queries, return entity instances, rather than database table records.
Here is a simple example of a dynamic native SQL query:
...
List<Customer> customers = (List<Customer>)em.createNativeQuery
("SELECT * FROM customers", jpqlexample.entities.Customer.class)
.getResultList();
Iterator i = customers.iterator();
Customer cust;
out.println("Customers: " + "<br/>");
while (i.hasNext()) {
cust = (Customer) i.next();
out.println(cust.getCust_name() +"<br/>");
}
...
JPQL is still evolving, and doesn't have many of those important features available in SQL. In the Defining JPQL Joins earlier section, you saw an example of
JPQL's incompleteness: you had to do a lot of work on your own because the JPQL's SUM aggregate function cannot take an arithmetic expression as the
parameter. In contrast, the SQL's SUM function doesn't have such a limitation. So, this is a good example of where replacing JPQL with native SQL could be
efficient. The following code illustrates how you might simplify things in this particular example by choosing native SQL over JPQL:
...
String sup_name ="Tortuga Trading";
BigDecimal sum = (List)em.createNativeQuery("SELECT SUM(p.price*l.quantity)
FROM orders o JOIN orderlineitems l ON o.pono=l.pono
JOIN products p ON l.prod_id=p.prod_id
JOIN suppliers s ON p.sup_id=s.sup_id WHERE sup_name =?1")
.setParameter(1, sup_name)
.getSingleResult();
out.println("The total cost of the ordered products supplied by Tortuga Trading: " + sum +"<br/>");
...
Among other things, the above example illustrates that you can bind arguments to native query parameters. In particular, you can bind arguments to positional
parameters in the same way as if you were dealing with a JPQL query.
The main disadvantage of native queries is complexity of result binding. In the example, the query produces a single result of a simple type, thus avoiding this
problem. In practice, however, you often have to deal with a result set of a complex type. In this case, you will have to declare an entity to which you can map
your native query, or define a complex result set mapped to multiple entities or to a blend of entities and scalar results.
Page 3 of 3
Download