|
In this issue
Welcome to the Enterprise Java Technologies Tech Tips for December 16, 2006. Here you'll get tips on using enterprise Java technologies and APIs, such as those in Java Platform, Enterprise Edition (Java EE). This issue covers: These tips were developed using the Java EE 5 SDK. You can download the SDK from the Java EE Downloads page. You can download the sample archive for the Pitfalls in Constructing Java Persistence Query Language Queries tip. You can download the sample archive for the EJB 3.0 Interceptors -- Continued tip. Any use of this code and/or information below is subject to the license terms.
One of the things that the Java Persistence API specifies is a query language that allows you to define queries over entities and their persistent state. The query language, called the Java Persistence Query Language, gives you a way to specify the semantics of queries in a portable way, independent of the particular database you're using in an enterprise environment. A recent Tech Tip introduced the Java Persistence Query Language and discussed some of its basic features. The following Tech Tip covers several things to be careful about when you construct Java Persistence Query Language queries. The tip assumes that you understand the basic terms and concepts of the Java Persistence API. If you don't, see "Chapter 24: Introduction to the Java Persistence API" in the Java EE 5 Tutorial. A sample package accompanies the tip. The code examples in the tip are taken from the source code of the sample (which is included in the package). The sample uses the Java EE 5 SDK. You can download the Java EE 5 SDK from the Java EE Downloads page . Entities Used in This Tip
Let's start with two persistent entities. These are the same two entities that were used in the Introduction to the Java
Persistence Query Language tip. The entities are @Entity @Table(name="CUSTOMER_TABLE") public class Customer implements Serializable{ public enum CustomerStatus {FULL_TIME, PART_TIME, CONTRACT}; @Id @Column(name="ID") private Integer customerId; @Column(name="CITY") private String city; @Column(name="NAME") private String name; @Enumerated(ORDINAL) @Column(name="STATUS") private CustomerStatus status; @OneToMany(mappedBy="customer") private <Collection> orders; ... } @Entity @Table(name="ORDER_TABLE") public class Order implements Serializable { @Id @Column(name="ID") private Integer orderId; @Column(name="quantity") private int quantity; @Column(name="totalPrice") private float totalPrice; @ManyToOne() @JoinColumn(name="CUST_ID") private Customer customer; ... }Here's the client code that creates instances of the entities, creates an entity manager to manage their persistence, and inserts the instances in a database: // Create an EntityManagerFactory for a persistence unit // called j2seEnvironment. EntityManagerFactory emf = Persistence.createEntityManagerFactory("j2seEnvironment"); // Create an Entity Manager EntityManager em = emf.createEntityManager(); // get a Transaction EntityTransaction tx = em.getTransaction(); // create a POJO instance of the Customer class Customer customer = new Customer(); customer.setCustomerId(new Integer(3)); customer.setName("SUN_SALE"); customer.setCity("SAN JOSE"); customer.setStatus(Customer.CustomerStatus.FULL_TIME); // create a POJO instance of the Order class // for this customer Order order = new Order(); order.setOrderId(new Integer(3)); order.setQuantity(new Integer(2)); order.setTotalPrice(new Float(22.30)); order.setCustomer(customer); // Make the Customer and Order instances persistent // and insert them into the database tx.begin(); em.persist(customer); em.persist(order); tx.commit();Now let's look at some Java Persistence Query Language queries that sometimes cause problems for developers, and run them against these entities. Using a
The Java Persistence Query Language supports various types of comparison expressions including the
A string_expression LIKE pattern_value [ESCAPE escape_character]
The
For example, the following customer.name LIKE 'f_r%'
compares the value of
The following customer.name LIKE 'f\_r%' ESCAPE '\'
compares the value of
// run a Java Persistence query using input parameters String ejbql = "SELECT c from Customer c WHERE c.name LIKE :pattern ESCAPE :esc"; Query query = em.createQuery(ejbql); query.setParameter("pattern", "\\_%"); query.setParameter("esc", '\\');
As you can see, two backslashes are used in the Here is equivalent code that does not use input parameters: String ejbql = "SELECT i FROM Item i WHERE i.name LIKE '\\_%' ESCAPE '\\'"; Query query = em.createQuery(ejbql); Using an
Programmers sometimes run into problems when they use an
An
Problems arise if you try to assign a numeric or string value based on an enum type. For example, in entity class String ejbql = "SELECT c FROM Customer c WHERE c.status = 1";
In fact, the query throws a The correct way to query the customer status is as follows: // run a Java Persistence query String ejbql = "SELECT c FROM Customer c WHERE c.status = :status"; Query query = em.createQuery(ejbql); query.setParameter( "status", Customer.CustomerStatus.FULL_TIME);
In the correct query an enum constant, Using an
An
The formal syntax for an in_expression ::= state_field_path_expression [NOT] IN (in_item {, in_item}* | subquery) in_item ::= literal | input_parameter
The
For example, the expression
When you use an
Here are some working examples: // IN with subquery String ejbql = "SELECT o FROM Order o WHERE o.customer.name IN (SELECT c.name FROM Customer c WHERE c.customerId = 3)"; Query query = em.createQuery(ejbql); // IN with set of integers String ejbql = "SELECT o FROM Order o WHERE o.customer.name IN (2, 3)"; Query query = em.createQuery(ejbql); // IN with set of strings String ejbql = "SELECT o FROM Order o WHERE o.customer.name IN ('foo', 'JIE_LENG')"; Query query = em.createQuery(ejbql);Using Native Queries The Java Persistence Query Language supports native queries. In other words, you can express a Java Persistence Query Language query using the SQL of the target database. Native queries are not guaranteed to be portable across databases. The result of a native query can consist of entities, scalar values, or a combination of entities and scalar values.
When multiple entity types are returned by a native query, the entities must be specified and mapped to the column results of
the SQL statement in a
Here is an example in which a native SQL query returns entities of a single entity class. The entity class that specifies the
type of the result is passed in as an argument. Because the native query returns a single entity type it does not require
a Query q = em.createNativeQuery( "SELECT o.id, o.quantity, o.customer " + "FROM Order o, Customer c " + "WHERE (o.customer = c.customerId) AND (c.name = 'SUN_SALE')", ejbql.models.Order.class); List orders = q.getResultList();
The following query returns multiple entity types and so it requires a @SqlResultSetMapping(name="OrderCustomerResults", entities={ @EntityResult( entityClass=ejbql.models.Order.class), @EntityResult(entityClass=ejbql.models.Customer.class) } ) @Entity public class Order { ... }The following code executes the native query: Query q = em.createNativeQuery( "SELECT o.id, o.quantity, c.customerId, c.name, c.city " + "FROM Order o, Customer c " + "WHERE (o.quantity > 25) AND (o.customer = c.customerId)", "OrderCustomerResults"); List result = q.getResultList(); int s = result.size(); for(int i = 0; i < s; i++){ Object obj = result.get(i); Object[] objectArray = (Object[]) obj; Object object1 = objectArray[0]; Object object2 = objectArray[1]; Order order = (Order) object1; Customer customer = (Customer)object2; }
Using
It's important to keep the following in mind if you use a
So the following query: String ejbql = "SELECT c FROM Customer c WHERE c.name = NULL"; Query query = em.createQuery(ejbql);
does not return any customers, even if there are customers who do not have a name. The reason is that the expression
The correct syntax for checking whether or not a {single_valued_path_expression | input_parameter} IS [NOT] NULLFor example, the following query: String ejbql = "SELECT c FROM Customer c WHERE c.name IS NULL"; Query query = em.createQuery(ejbql);returns all the customers that do not have a name. For More Information The Java Persistence Query Language is a full structured query language and includes language features for bulk update and delete operations, outer join operations, projection, and subqueries. For a description of the language see "Chapter 27: The Java Persistence Query Language" in the Java EE 5 Tutorial. Running the Sample Code To install and run the sample code that accompanies this tip:
In response you should see output similar to this: ... start-db: [exec] Database started in Network Server mode on host ... and port ... [exec] Starting database in the background. Log redirected to ... [exec] Command start-database executed successfully. Jie Lin Leng is a member of the Java Persistence Engineering group at Sun. She is currently involved in the development of the Java Persistence Query Language for EJB 3.0.
One of the new features introduced in the EJB 3.0 specification is interceptors. An interceptor is a method that you can interpose in the invocation flow of an enterprise bean. You can define an interceptor to intercept an enterprise bean's business methods -- the interceptor method runs before any of the bean's business methods are invoked. Or you can define an interceptor to intercept lifecycle events for an enterprise bean -- the interceptor method runs as a callback method for the bean's lifecycle events. A recent Tech Tip introduced the concept of EJB 3.0 interceptors and described how to write multiple interceptors and chain them together. The following Tech Tip discusses how to add interceptors at the method level and further discusses interceptor ordering. A sample package accompanies the tip. The code examples in the tip are taken from the source code of the sample (which is included in the package). The sample uses the Java EE 5 SDK. You can download the Java EE 5 SDK from the Java EE Downloads page. An Example Bean
To demonstrate the concepts in this tip, let's use a simple stateless session bean named NumberUtilbean. The bean provides
a set of simple utility methods, and implements a business interface named
Here is what package com.sun.techtip2.interceptor; import javax.ejb.Remote; @Remote public interface NumberUtil { public byte[] intToBytes(int val); public int bytesToInt(byte[] data); public boolean isOddNumber(int val); } And here is NumberUtilBean: package com.sun.techtip2.interceptor; import javax.ejb.Stateless @Stateless public class NumberUtilBean implements NumberUtil { public byte[] intToBytes(int val) {...} public int bytesToInt(byte[] data) {...} public boolean isOddNumber(int val) {...} }
Note that the Class Level Interceptors
The previous tip showed how to define an interceptor and add it to a bean. It included as an example of an interceptor,
a method profiler that prints the time it takes to run an intercepted business method. Let's create another method
profiler that will be used as an interceptor. The new method profiler prints the time taken by each of the methods in
As explained in the previous tip, you define an interceptor with an
Here is the new method profiler, package com.sun.techtip2.interceptor; import javax.interceptor.AroundInvoke; public class MethodProfileInterceptor { public MethodProfiler () { } @AroundInvoke private Object recordStat(InvocationContext invCtx) throws Exception { long t1 = System.currentTimeMillis(); try { invCtx.proceed(); //Continue execution finally { long t2 = System.currentTimeMillis(); System.out.println(invCtx.getMethod().getName() + " took: " + ((t2 - t1)/1000.0 + " seconds"); } } }
Here is package com.sun.techtip2.interceptor; import javax.ejb.Stateless import javax.interceptor.Interceptors; @Interceptors( {com.sun.techtip2.interceptor.MethodProfileInterceptor.class}) @Stateless public class NumberUtilBean implements NumberUtil { public byte[] intToBytes(int val) {...} public int bytesToInt(byte[] data) {...} public boolean isOddNumber(int data) {...} } Method Level Interceptors
Say you want to add an interceptor to
But what if you want to restrict the interceptor to a specific method in the bean? For example, suppose you want to validate
that the parameter passed to the
Here is the code for an interceptor that checks if a parameter is not null:
Notice how
Here is the Changing the Invocation Order of Interceptors It's possible to have a class in which interceptors are defined at both the class level and the method level. In that case, it's important to know the order in which the interceptors are invoked. The EJB 3.0 specification declares the following invocation order if both class-level and method-level interceptors are specified:
Excluding Interceptors
In the method profiler example, the class-level interceptor,
You could exclude the interceptor from running before the
But there is an easier way to exclude interceptors from a method. You can use the
Here is the
Now Running the Sample Code
Summary EJB 3.0 interceptors give you an elegant way to extend the functionality of enterprise beans. They are especially useful for implementing crosscutting operations in a portable way. A previous Tech Tip introduced interceptors and demonstrated the use of a class-level interceptors. Class-level interceptors are executed before every business method in a bean. This tip introduced method-level interceptors and demonstrated how these interceptors are executed for a specific method in addition to class-lLevel interceptors.
The tip also showed how to use the There are more advanced topics related to EJB interceptors, such as default interceptors and excluding default interceptors. Also, interceptors can declare dependency injection. This allows the container to inject resources and enterprise beans into interceptors. About the Author Mahesh Kannan is member of the EJB container team. He has been involved with Java EE development for the last 6 years. | ||||||||||||
|
| ||||||||||||