|
SYS-CON Magazines
|
Top Linux Links You Must Click On
Feature Portable Persistence Using the EJB 3.0 Java Persistence API
The time for standardizing persistent POJOs has come
Oct. 20, 2006 02:00 PM
Persistence Operations
Timestamp depTime = Timestamp.valueOf("2006-09-30 05:07:0"); 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 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( 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");
Vendor-specific Features 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 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. Reader Feedback: Page 1 of 1
Subscribe to our RSS feeds now and receive the next article instantly!
Subscribe to the World's Most Powerful Newsletters
|
||||||||||||