|
標簽: 瀏覽(12150) 日期:2007-04-30 by Sean Brydon and Inderjeet Singh The Java Persistence API (or simply Java Persistence) provides a POJO-based domain model for Java EE 5 applications. It handles all of the details of how relational data is mapped to Java objects, and it standardizes object-relational mapping. Often applications that use Java Persistence execute queries that return a collection of objects. Java Platform, Standard Edition (Java SE) 5 introduced a new feature called generics that lets you specify the type of objects in collections. If you use Java Persistence in your applications, you can take advantage of generics to gain some extra benefits for your code such as type safety. In this tip you`ll learn how to refactor applications to use generics. A sample web application 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 Basics of Generics Although not the only thing it`s used for, generics gives you a way to communicate the type of objects in a collection. Using generics, you specify a collection with a parameter that represents a class. For example, the following declares that the method getAllItems returns a collection of objects of type Item: public List<Item> getAllItems(){...}
Using generics is a good practice for the following reasons:
- It allows the compiler to do type checking and catch potential bugs in your code. If you don`t use generics, you won`t find those bugs until the application runs.
- It can help identify bugs before deployment.
- It can make your code more readable. When other developers read your code, they can see what type of objects are expected in a collection.
- Without generics, the client code has to typecast each object it gets from a collection.
A Persistence Example To illustrate using generics with Java Persistence, let`s looks at a simple Java Persistence example. In this example a JSP page makes a request to find all the items in a database and show them in an XML document. The request is processed as follows: - The main JSP page (index.jsp) sends the request to a servlet.
- The servlet directs the request to a Facade.
- The Facade creates a query using the Java Persistence Query Language to find all items in the database. The Facade could be a web component such as a servlet or a session bean.
- The Java Persistence Runtime executes the query, does the object-relational mapping, builds a list of
Items, and performs some other tasks.
- The database executes an SQL query and returns a list of results, each of which represents one item (that is, one row) in the database.
- The Facade passes the
List to the servlet.
- The servlet returns the
List to the JSP page (itemsxml.jsp ), which displays all the items as XML.
Let`s start by looking at the Facade. Here is what the code for the request in the Facade looks like: public List getAllItemsInPlainCollection(){
EntityManager em = emf.createEntityManager();
Query query = em.createQuery(
"SELECT OBJECT(i) FROM Item i");
List items = query.getResultList();
em.close();
return items;
}
To "generify" the code, all you have to do is make two additions to it as follows: public List<Item> getAllItemsInTypedCollection(){
EntityManager em = emf.createEntityManager();
Query query = em.createQuery(
"SELECT OBJECT(i) FROM Item i");
List<Itemgt; items = query.getResultList();
em.close();
return items;
}
The changed code specifies that the return value will contain objects of type Item. Now let`s look at the pertinent code in the servlet: public void doGet(HttpServletRequest request,
HttpServletResponse response)
...
if (selectedURL.equals("findallitems.do")) {
List items = cf.getAllItems();
request.setAttribute("items", items);
}
Now the itemsxml.jsp page: ...
<Items>
<% List items = (List)request.getAttribute("items");
for (Object o : items) {
Item item = (Item) o; %>
<Item>
<Name><%=item.getName()%> </Name>
<ListPrice>$<%=item.getListPrice()%> </ListPrice>
<Description><%=item.getDescription()%>
</Description>
</Item>
<% } %>
</Items>
...
Notice how each item in the list of items must be cast to the proper type. By using generics, you can avoid the need for casting, as shown in the following code: ...
<Items>
<% List<Item> items = (List<Item>)
request.getAttribute("items");
for (Item item : items) { %>
<Item>
<Name><%= item.getName() %></Name>
<ListPrice>$<%=item.getListPrice()%></ListPrice>
<Description><%=item.getDescription()%>
</Description>
</Item>
<% } %>
</Items>
...
And finally, here is part of the code for the Item class, a simple persistence object. As mentioned previously, the query in this example returns a list of these Item objects, each of which represents a row in the database. import javax.persistence.*;
@Entity
public class Item implements java.io.Serializable {
private int itemID;
private String name;
...other fields listed here
public Item() { }
@Id
public int getItemID() {
return itemID;
}
public String getName() {
return name;
}
public void setItemID(int itemID) {
this.itemID = itemID;
}
public void setName(String name) {
this.name = name;
}
//...other getters and setters methods
}
Generics Collections are Not Polymorphic When you use generics, you need to be aware that generics collections are not polymorphic. If you`re new to generics, you might assume that you`re allowed to assign an object of type List<Item> to an object reference of type List<Object>. Somewhat counter intuitively, this is not the case. For example, the following code produces a compile-time error: public List<Object> getAllItems() {
// .... initialize the query object appropriately
List<Item> results = query.getResultList();
// the following return value will generate
// a compile-time error
return query;
}
The code produces a compile-time error to protect the type integrity of the collection. Remember, the collection is supposed to be a list of Item objects only. If the code above was valid, you could add other types of objects, such as an Address object, to the collection through the add method of List<Object>. One alternative and legal way of expressing the same intent is to use generics with wildcards. For example: public List<?> getAllItemsasObjects() {
// .... initialize the query object appropriately
List<Item> results = query.getResultList();
return query;
}
Notice here that the return value is not a collection of arbitrary objects. It is a collection of unknown objects, where nothing can be assumed about their types. In fact, any method of Collections, such as the add method, that needs a specific type results in a compile time error. The only methods that can be used are those that assume the type to be java.lang.Object. Preventing Spurious Warnings Another thing to keep in mind is that the current version (1.0) of the Java Persistence API does not take advantage of generics to ensure better type safety. This can result in spurious warnings if your code uses generics. The following code elicits the problem: public List<Item> getAllItemsInTypedCollection(){
EntityManager em = emf.createEntityManager();
Query query = em.createQuery(
"SELECT OBJECT(i) FROM Item i");
List<Item> items = query.getResultList();
em.close();
return items;
}
If you compile the code using the -Xlint:unchecked option it generates the warning: warning: [unchecked] unchecked conversion
found : java.util.List
required:
java.util.List<com.sun.javaee.blueprints.autoid.model.Item>
List<Item> items = query.getResultList();
If you`re not familiar with the -Xlint:unchecked option, it tells the compiler to give more detail for unchecked conversion warnings that are mandated by the Java Language Specification. The warning is generated because query.getResultList() returns a non-generic version of List. However, the items variable is of type List<Item>. This warning is spurious because the actual result of the query is a List of Item objects. Using a typecast of (List<Item> ) does not solve the problem because the compiler does not preserve the generics type information for the runtime. In a future version of the Java Persistence API, the javax.persistence.Query class will likely change to better support generics. Until then, the best way to eliminate the spurious warning is to use the @SuppressWarnings annotation as shown in the following code example: @SuppressWarnings("unchecked")
public List<Item> getAllItemsInTypedCollection(){
EntityManager em = emf.createEntityManager();
Query query = em.createQuery(
"SELECT OBJECT(i) FROM Item i");
List<Item> items = query.getResultList();
em.close();
return items;
}
For Further Information For additional information about generics and the Java Persistence API, see: Running the Sample Code The sample code for this tip is available as a NetBeans project. You can build and run the sample code using the NetBeans IDE. You can also build and run the sample code from the command line. Instructions for building and running the sample code from the command line are in the README file in the sample package for the tip. You can build and run the sample code using the NetBeans IDE as follows: - If you haven`t already done so, download and install the NetBeans IDE. NetBeans IDE 5.5 with the NetBeans Enterprise Pack 5.5 is available in Java EE 5 SDK Update 2, which you can download from the Java EE Downloads Page.
- Download the sample package for the tip and extract its contents. You should now see the newly extracted directory as
<sample_install_dir>/bp-persistence-webonly, where <sample_install_dir> is the directory where you installed the sample package. For example, if you extracted the contents to C:\ on a Windows machine, then your newly created directory should be at C:\bp-persistence-webonly.
- Start the NetBeans IDE.
- Open the bp-persistence-webonly project. You`ll be prompted to resolve the missing reference to javadb.root. Close the prompt message. Resolve the missing reference as follows:
- Right click on the bp-persistence-webonly node in the Projects window.
- Select "Resolve Reference Problems ..."
- Click the Resolve ... button.
- Browse to the
javadb directory below the installation directory of Sun Application Server. - Click the Close button.
- Start the Java DB database as follows:
- Select "Java DB Database" in the Tools menu.
- Select "Start Java DB Server."
- Build the project as follows:
- Right click the bp-persistence-webonly node in the Projects window.
- Select "Clean and Build Project."
- Run the project as follows:
- Right click the bp-persistence-webonly node in the Projects window.
- Select "Run Project."
When you run the project (at URL http://localhost:8080/bp-persistence-webonly/) your browser should display the opening page of the Java Persistence Sample Application. About the Authors Sean Brydon is an engineer with Sun Microsystems where he is the technical lead for the Java BluePrints program. He has been involved with the Java BluePrints since its inception. He is an author of the Addison-Wesley Java-series books, "Designing Enterprise Applications with the Java 2 Platform, Enterprise Edition," and "Designing Web Services with the J2EE 1.4 Platform." He is a regular speaker on enterprise application design. Sean has also been involved in designing and developing the Java Pet Store 2.0 reference application and the Java BluePrints Solutions Catalog. Inderjeet Singh is a software engineer at Google, Inc. Prior to joining Google, Inderjeet spent 10 years at Sun Microsystems as a senior staff engineer where he was an architect for the Java EE SDK, Java Application Platform SDK, and the Java BluePrints program.
|