| By Mike Keith, Merrick Schincariol | Article Rating: |
|
| October 20, 2006 02:00 PM EDT | Reads: |
20,169 |
Persistence Operations
The way to operate on entities in JPA is by invoking a method on an entity manager
and passing the entity as an argument to the method. The entity manager
provides a common interface for entity operations and provides entity
management within a transactional or even longer scoped context. For
example, to persist a Flight entity, one only has to have access to an
entity manager and invoke the persist() method on it, passing in the
Flight entity as follows:
Timestamp depTime = Timestamp.valueOf("2006-09-30 05:07:0");
Flight newFlight = new Flight(552, depTime, "San Francisco");
em.persist(newFlight);.
When the transaction commits, the entity will be guaranteed to be committed to persistence storage. Likewise, one may use an entity manager to obtain a pre-existing instance of a Flight entity based on its flight id:
Flight flight552 = em.find(Flight.class, 552);
The entity manager has a complete and understandable API that is the main gateway to using JPA. However, persistence providers implement entity managers, and because allowances are made in the specification for different kinds of implementations, the semantics are sometimes a little looser than what you might expect. For example, in the persist() operation above we mentioned that the entity will be guaranteed to be committed to the database when the transaction commits. We didn't say when the data actually gets written to the database because the specification actually allows the provider either to eagerly write it or defer the write until the transaction commits. The only thing the user can rely on is that by the time the transaction has successfully committed the data will be in the database. It turns out that almost all vendors will actually defer the write because it's more efficient, but users who rely on this fact could be in trouble if they change providers (performance and scalability degradation aside).
The entity manager is a scaled-down version of the session API that has long been used in TopLink or a similar session API in Hibernate. The most common and useful operations on these session APIs have been normalized into the entity manager and represent an API that spans all persistence providers. Programming to the JPA EntityManager API will enable an application to be portable across these providers and prevent non-standard or proprietary features from slipping in.
Queries
To execute a query in JPA a query object
must first be obtained from an entity manager. The query criteria is
specified either dynamically in code or statically in metadata, and is
normally expressed in terms of the JPA query language called the Java
Persistence Query Language (JPQL). Queries are executed and depending
upon the query the results may be returned either as entities,
temporary non-entity objects, or even report data.
JPQL is based on EJB QL but is more powerful and more flexible. It still provides an abstraction language that's used to express queries in terms of entity state and relationships, but is expanded to include a host of new language features including a larger set of functions, outer joins, named parameters, sub-selects, aggregation, bulk updates and deletes, and much more. Because JPQL is a database-neutral language, queries expressed in JPQL are not only portable with respect to persistence providers but also across databases.
Queries may also be created using native SQL. This will typically reduce portability across databases but won't affect persistence provider portability. Queries that use SQL will produce uniform results and are mapped to entities in a standard way. SQL queries are discouraged, however, unless really needed, since they're less maintainable and result in a tighter coupling to the database.
A typical dynamic query to return a list of all of the flights going to a specific destination would be created and executed the following way. The destination is a named parameter that is bound to an argument before being executed, so the same query instance can be reused for querying different destinations. We'll find all the flights going to San Francisco.
Query q = em.createQuery(
"SELECT f FROM Flight f WHERE f.dest=:destination");
q.setParameter("destination", "San Francisco");
List sfResults = q.getResultList();
While this query is completely portable in terms of its execution, what about the semantics of the query, or the results that are returned? Could the results differ depending upon the context in which it's executed? For example, if there was a transaction in progress and a new flight to San Francisco was added in the transaction, is that flight going to be returned by all providers? Remember that if the transaction hasn't been committed yet, depending upon the implementation, the new flight may not even have been written to the database.
The answer is that there is something called a flush mode that determines whether all changes in the transaction have been written out. By ensuring that the flush mode setting causes a flush to occur before transactional queries are executed, the results will always be the same regardless of the persistence implementation. In fact, to achieve portability and query results that most people would expect, the flush mode is set this way by default. To avoid the performance overhead of issuing a flush before a query, the flush mode is often changed. However, to ensure portability this should only be done when it's known that any entities modified earlier in the transaction won't affect the outcome of the query. Alternatively, queries can be executed outside of transactions, typically improving the performance of the query in the process.
One of the best practices for creating queries is to define them statically using the named query facility. Named queries are queries that are defined in annotations or XML and offer an additional form of application portability. The query criteria may be separated from the application code and filled in using JPQL, SQL, or any proprietary query language, such as the TopLink expression framework. The above query could be defined as a named query by defining the following annotation that specifies the name of the query and the query criteria as follows:
@NamedQuery(name="Flight.findByDestination", query="SELECT f FROM Flight f WHERE f.dest=:destination")
The query is invoked by obtaining an instance of the named query, binding the parameter, then executing it, similar to the dynamic query example above:
Query q = em.createQuery("Flight.findByDestination");
q.setParameter("destination", "San Francisco");
List sfResults = q.getResultList();
Vendor-specific Features
It's not unusual that a
large application has requirements that the JPA 1.0 can't fulfill.
After all, the first release of JPA included a lot of features, but as
mentioned above, not everything was added. There are still a number of
features up for discussion and possible inclusion in subsequent JPA
releases that at this point in time are vendor-specific. Pessimistic
locking is an example of a feature that's not usually required but on
rare occasions is absolutely necessary.
Vendor-specific features can be accessed a number of different ways; some of them better than others. As we saw in the persistence unit metadata section, JPA does provide some mechanisms for vendors to incorporate additional features using standard APIs and the persistence properties are one such way. Query hints are also a way for additional query features to be accessed either programmatically or through metadata. Vendors can define their own query hints that users can add to named or dynamic queries before they're executed.
Additional XML files and annotations are another common way for users to add vendor-specific metadata. Proprietary annotations tend to be more dangerous than XML because they introduce compile-time dependencies in addition to any runtime dependencies that may exist.
In code the vendor-specific EntityManager implementation class can be retrieved by calling a special method on EntityManager and casting the result. A Query can also be cast to a proprietary interface or class. These practices should be used with care because they introduce code dependencies into the application.
Regardless of how the feature is used, the best way to organize vendor-specific feature use is to try to localize it to specific metadata and code areas. This way if porting is required it's easy to find the metadata or code that might need to change.
Conclusion
We've looked at only a few of the
features of the EJB 3.0 Java Persistence API, but we have already seen
how it can provide applications with a modern and portable platform for
object-relational mapping and persistence. By combining the principal
and most significant features of the major persistence solutions on the
market and in the public domain, JPA can be used to write applications
without being bound to any particular persistence provider or vendor.
The underlying persistence implementation could be changed with few or
no changes to the application code that uses it.
We did see a few examples of how some of the implementations may differ from each other though, so application developers should still be alert to the nuances of the provider implementation. A simple understanding of the API is all that's needed to develop simple persistence applications. However, to develop complex portable applications, a more thorough understanding is critical. For in-depth coverage on the features in JPA, as well as the portability issues of the API, we refer the reader to Pro EJB 3: Java Persistence API.
Published October 20, 2006 Reads 20,169
Copyright © 2006 SYS-CON Media, Inc. — All Rights Reserved.
Syndicated stories and blog feeds, all rights reserved by the author.
About Mike Keith
Mike Keith has more than 15 years of teaching, research and practical experience in object-oriented and distributed systems, specializing in object persistence. He was the co-specification lead for EJB 3.0 (JSR 220), a member of the Java EE 5 expert group (JSR 244) and co-authored the premier JPA reference book Pro EJB 3: Java Persistence API. Mike is currently a persistence architect for Oracle and a popular speaker at numerous conferences and events around the world.
About Merrick Schincariol
Merrick Schincariol is a senior engineer for the Oracle OC4J Java EE Container. He was a lead engineer for Oracle's EJB 3.0 release and co-author of Pro EJB 3: Java Persistence API. Before joining Oracle, Merrick developed enterprise and large-scale systems for the telecomunications industry.
- Performance of Java Compilers: An Empirical Study
- Java Kicks Ruby on Rails in the Butt
- Ulitzer’s Amazing First 30 Days in Public Beta
- 1st Annual Government IT Expo: Call for Papers Deadline July 15
- REA Is Where RIA Becomes the Norm
- Why an Application Grid?
- Will Ulitzer Dominate News Content on The Web? -Gartner
- Clear Toolkit 4: The Road Map
- Profiling Netbeans within Amazon EC2
- Java Persistence on the Grid: Approaches to Integration
- Performance of Java Compilers: An Empirical Study
- Java Kicks Ruby on Rails in the Butt
- Developing Rich Client Applications Using Swing - II
- The Right Time for Real Time Java
- Xpress Suite Adds Automatic Java to iPhone Conversion
- Ulitzer’s Amazing First 30 Days in Public Beta
- Initial Thoughts on IBM Acquisition of Sun Microsystems
- 1st Annual Government IT Expo: Call for Papers Deadline July 15
- Maximizing Java Performance with Bespoke Programming
- REA Is Where RIA Becomes the Norm
- A Cup of AJAX? Nay, Just Regular Java Please
- Java Developer's Journal Exclusive: 2006 "JDJ Editors' Choice" Awards
- The i-Technology Right Stuff
- JavaServer Faces (JSF) vs Struts
- Rich Internet Applications with Adobe Flex 2 and Java
- Java vs C++ "Shootout" Revisited
- Bean-Managed Persistence Using a Proxy List
- Reporting Made Easy with JasperReports and Hibernate
- What's New in Eclipse?
- Creating a Pet Store Application with JavaServer Faces, Spring, and Hibernate





































