The Java Persistence API offers a powerful standard for persisting POJOs.It includes all the most important features which you would expect from an object-relational mapping tool, but there are still some areas where you might need to use vendor-specific features. In this tutorial, we will show how to use Hibernate-specific features for validating and querying while using the standard API for the rest.
This tutorial assumes you have some basic knowledge of, or programming experience with, the following technologies:
This tutorial is partly based on the Using Hibernate With Java Persistence API tutorial, you might want to go through it first.
Before you begin, you need to install the following software on your computer:
First we will create a library in the IDE for the Hibernate entity manager.
<property name="hibernate.dialect" value="org.hibernate.dialect.DerbyDialect" />
Now we can quickly generate JSF pages for the entity classes with the NetBeans IDE CRUD generation.
At this point we will try to run the application and see whether everything is working as expected.
When creating a new customer you might have noticed that values were not validated before attempting to persist the newly created customer. We could use the validation facilities in JSF for ensuring that only correct values are inserted, but since this tutorial is about Hibernate we will instead demonstrate how to use the Hibernate validation framework. This approach has an additional advantage that the validation rules need to be specified only once even if another type of client is added.
Open Customer.java in the editor and add the following annotations on its member variables:
@Column(name = "STATE") @Length(min=2, max=2, message="not a valid state") private String state; @Column(name = "EMAIL") @Email private String email;For simplicity's sake we will only add validation to state and email properties. The @Length annotation validates that the length of the property will be within the range specified by min and max attributes. We apply it on the state property to make sure that it is exactly 2 characters long. We also specify a value for the message attribute, it will be the shown error message when the validation fails. The @Email annotation in turn validates that the property represents a valid email address, as you might have guessed.
Now that we have our domain object annotated, we need to add handling of validation errors to our controller class. So open CustomerController.java and add the following method there:
/** * Validates the given customer. * @return true if there were no validation errors, false otherwise. */ private boolean validate(Customer customer){ ClassValidator customerValidator = new ClassValidator(Customer.class); // get the invalid values InvalidValue[] msgs = customerValidator.getInvalidValues(customer); if (msgs.length > 0){ for(InvalidValue msg : msgs){ // add an error message for each invalid value, these // messages will be shown to the user addErrorMessage(msg.getMessage()); } return false; } return true; }This method will first create a class validator for our Customer class and then process the validation rules we specified earlier when. We collect the invalid value messages and add each of them as error messages to the FacesContext (this is done by the addErrorMessage method). If there were no validation errors the will return true, false otherwise. Of course, as such this method is not very useful unless we invoke it in the right places. We probably want to validate the values both when a new customer is created and when an existing customer is edited. So let's first modify the create method to check whether there were any validation errors before attempting to persist:
public String create() { if (!validate(customer)){ // return to the input page if there were any validation errors return null; } EntityManager em = getEntityManager(); try { utx.begin(); em.joinTransaction(); em.persist(customer); ...
As you can see, we return null if any errors were found - this means JSF will display the same page again. Make a similar modification to the edit method as well and run the application. Try to create a new customer with an invalid email address and with a 3 characters long state code. This is what you should see:
While the Java Persistence QL is an impressive query language, there are cases when a different kind of API is more suitable. Luckily, in addition to JPQL support, Hibernate features a criteria query API which you can leverage for the cases it is needed and stick to the standard API elsewhere in the application. In the following example we will demonstrate the Query By Example approach using Hibernate's Criteria API.
First we need to create a new page for our new query functionality. Create a new page named Query.jsp in the customer folder and paste the following to it:
<%@page contentType="text/html"%> <%@page pageEncoding="UTF-8"%> <%@taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Query By Example</title> </head> <body> <f:view> <h1>Query By Example</h1> <h:messages errorStyle="color: red" infoStyle="color: green" layout="table"/> <h:form> <h:panelGrid columns="2"> <h:outputText value="Zip:"/> <h:inputText id="zip" value="#{customer.customer.zip}" converter="stringConverter" title="Zip" /> <h:outputText value="Name:"/> <h:inputText id="name" value="#{customer.customer.name}" converter="stringConverter" title="Name" /> <h:outputText value="State:"/> <h:inputText id="state" value="#{customer.customer.state}" converter="stringConverter" title="State" /> </h:panelGrid> <h:commandLink action="#{customer.queryByExample}" value="Search"/> </h:form> <h:form> <a href="/HibernateWithJPA/index.jsp">Back to index</a> <br> <h:dataTable value='#{customer.model}' var='item' border="1" cellpadding="2" cellspacing="0"> <h:column> <f:facet name="header"> <h:outputText value="CustomerId"/> </f:facet> <h:commandLink action="#{customer.detailSetup}" value="#{item.customerId}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Zip"/> </f:facet> <h:outputText value="#{item.zip}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Name"/> </f:facet> <h:outputText value="#{item.name}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Addressline1"/> </f:facet> <h:outputText value="#{item.addressline1}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Addressline2"/> </f:facet> <h:outputText value="#{item.addressline2}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="City"/> </f:facet> <h:outputText value="#{item.city}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="State"/> </f:facet> <h:outputText value="#{item.state}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Phone"/> </f:facet> <h:outputText value="#{item.phone}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Fax"/> </f:facet> <h:outputText value="#{item.fax}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Email"/> </f:facet> <h:outputText value="#{item.email}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="CreditLimit"/> </f:facet> <h:outputText value="#{item.creditLimit}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="DiscountCode"/> </f:facet> <h:outputText value="#{item.discountCode}"/> </h:column> <h:column> <h:commandLink value="Destroy" action="#{customer.destroy}"> <f:param name="customerId" value="#{item.customerId}"/> </h:commandLink> <h:outputText value=" "/> <h:commandLink value="Edit" action="#{customer.editSetup}"> <f:param name="customerId" value="#{item.customerId}"/> </h:commandLink> </h:column> </h:dataTable> </h:form> </f:view> </body> </html>There are a couple of things to note here. Firstly, we have specified "stringConverter" converter for the input fields. Secondly, the 'search' link will try to execute customer.queryByExample method, which we haven't yet implemented. Thirdly, the data table uses customer.model as its underlying model. We will get back to these in a minute, but before that we still need to create a link to our new page. To keep things simple, we will just add it to the customer/List.jsp page, right after the link to the New Customer page:
<h:commandLink action="#{customer.createSetup}" value="New Customer"/> <br> <h:commandLink action="#{customer.querySetup}" value="Query Customers"/>
Now, this link will cause querySetup method to be invoked in the CustomerController.java, so let's implement it next. To do that, add the following to the CustomerController.java:
public String querySetup(){ this.customer = new Customer(); this.model = null; return "customer_query"; }
And to complete the UI side of things, we still need to add a navigation rule to faces-config.xml:
<navigation-rule> <navigation-case> <from-outcome>customer_query</from-outcome> <to-view-id>/customer/Query.jsp</to-view-id> </navigation-case> </navigation-rule>
What is still missing is the actual implementation of the query, and the converter we mentioned earlier. Let's tackle the implementation of the query method first. In the Query.jsp page we defined that the 'Search' link will invoke customer.queryByExample method and that customer.model will be used for the data table. To satisfy the latter, we just need to create a getter for the model in CustomerController.java - press ctrl-space and choose 'create getter getModel for field model'. After that, add the following method:
/** * Queries customers based on the values in our <code>customer</code>. */ public String queryByExample(){ // get the native hibernate session Session session = (Session) getEntityManager().getDelegate(); // create an example from our customer, exclude all zero valued numeric properties Example customerExample = Example.create(customer).excludeZeroes(); // create criteria based on the customer example Criteria criteria = session.createCriteria(Customer.class).add(customerExample); // perform the query and set the result to our model. this.model = new ListDataModel(criteria.list()); return "customer_query"; }
You can see how easily you can access Hibernate's native API - just invoke getDelegate() on the entity manager and cast it to org.hibernate.Session. Once we have the access to Session we can take advantage of the Criteria API. In the above method we create an example criteria based on the customer and execute the query. If you are not familiar with the Criteria API, it is probably worth explaining a bit. By default, all null values will be excluded, which means that the properties on our customer that were null, will not be part of the criteria. In addition, we specify that all zero valued numeric properties will be excluded as well. Here it gets a bit complicated: since JSF by default converts strings without values to empty strings (instead of nulls), we need to create a special converter for dealing with the conversion of strings from the query page. The implementation of the converter is as simple as:
package sample.controller; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.convert.Converter; /** * A converter for string values that does not * convert <code>null</code> values to empty strings. */ public class StringConverter implements Converter{ /** Creates a new instance of StringConverter */ public StringConverter() { } public Object getAsObject(FacesContext context, UIComponent component, String value) { if(value == null || "".equals(value)){ return null; } return value; } public String getAsString(FacesContext context, UIComponent component, Object value) { return value != null ? value.toString() : null; } }
Don't forget to register the converter in faces-config.xml:
<converter> <converter-id>stringConverter</converter-id>> <converter-class>sample.controller.StringConverter</converter-class> </converter>
Finally, we are ready to run the application once again and test the new functionality:
Download the source code for this tutorial here
HibernateWithJPA.zip | ![]() |
46352 bytes |
jsf-from-entities.JPG | ![]() |
50178 bytes |
list-of-customers.JPG | ![]() |
185621 bytes |
not-valid-customer.JPG | ![]() |
32670 bytes |
query-by-example.JPG | ![]() |
81683 bytes |
query-by-example.PNG | ![]() |
152412 bytes |
query-by-example.png | ![]() |
152355 bytes |
jsf-from-entities.png | ![]() |
151411 bytes |
list-of-customers.png | ![]() |
314586 bytes |
not-valid-customer.png | ![]() |
84493 bytes |
One of the main features of the new Java Persistence API that was introduced with the Java EE 5 platform is that you can plug in any persistence provider that you want. The Sun Java System Application Server 9 ships with Oracles's Toplink persistence provider. In this tutorial, we will install the Hibernate persistence provider on the application server and use it to develop a quick application that accesses a database with the Java Persistence API.
Before we start, we have to register the application server with the IDE and copy over the Hibernate JAR files to the application server's lib folder. We also have to create a library in the IDE for the Hibernate entity manager.
Now that we have added the Hibernate libraries to the lib directory of our application server, we can create the entity classes and web interface. We will create a web application named HibernateApp with entity classes and a JSF interface. We will use the NetBeans CRUD facilities.
<persistence-unit name="HibernateApp" transaction-type="JTA"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <jta-data-source>jdbc/sample</jta-data-source> <properties> <property name="hibernate.show_sql" value="true" /> </properties> </persistence-unit> </persistence>
Now we can quickly generate JSF pages for the entity classes with the NetBeans IDE CRUD generation.
Open CustomerController.java and DiscountCodeController.java in the Source Editor. Each of the files contains create, edit and destroy methods with the following code:
EntityManager em = getEntityManager(); try { utx.begin();
Modify the code in each of the methods to change the order in which we get the entity manager.
EntityManager em = null; try { utx.begin(); em = getEntityManager();
For example, the modified code in the create method of CustomerController.java should look like the following (changes in bold):
public String create() { EntityManager em = null; try { utx.begin(); em = getEntityManager(); em.persist(customer); utx.commit(); ...
After creating the JSF pages and modifying the code in the controller classes, you should be able to run and test the application.
Now we need to run the project to test whether the connection to our database is working correctly.
For more information about using NetBeans IDE 5.5 to develop Java EE applications, see the following resources:
To send comments and suggestions, get support, and keep informed on the latest developments on the NetBeans IDE Java EE development features, join the nbj2ee@netbeans.org mailing list.
Java API for XML Web Services (JAX-WS) 2.0,JSR 224 是 Java EE 5 平臺的重要組成部分。作為 Java API for XML-based RPC 1.1 (JAX-RPC) 的后續(xù)發(fā)行版本,JAX-WS 簡化了使用 Java 技術開發(fā) Web 服務的工作。并且通過對多種協(xié)議(如 SOAP 1.1、SOAP 1.2、XML)的支持,以及提供用于支持其他協(xié)議和 HTTP 的工具,它解決了 JAX-RPC 1.1 中存在的一些問題。JAX-WS 使用 JAXB 2.0 提供數(shù)據(jù)綁定服務,并支持通過定制來控制生成的服務端點接口。通過對標注的支持,JAX-WS 簡化了 Web 服務開發(fā),并縮小了運行時 JAR 文件的大小。
本文檔向您介紹了有關使用 IDE 開發(fā) JAX-WS Web 服務以及在三個不同的客戶端(Java SE 應用程序中的 Java 類、Web 應用程序中的 Servlet 或 JSP 頁)中使用該服務的基礎知識。在本文檔中創(chuàng)建的三個客戶端是獨立的應用程序,它們都使用同一個 Web 服務。
預計持續(xù)時間:25 分鐘
在開始之前,您需要在計算機中安裝以下軟件:
如果尚未注冊 Sun Java System Application Server 9.0 的實例,則必須先進行注冊,然后才可以著手開發(fā) Java EE 5 應用程序:
注意:也可以部署到 Tomcat Web Server,但由于它僅具有一個 Web 容器,因此應在下一部分創(chuàng)建 Web 應用程序,而不是 EJB 模塊。與 JSR-109 Web 服務不同的是,JAX-WS Web 服務可以成功地部署到 Tomcat Web 容器中。
本練習的目的是創(chuàng)建一個適用于您決定使用的部署容器的項目。在建立項目后,您將在其中創(chuàng)建 Web 服務。
您可以在 Web 容器或 EJB 容器中部署 Web 服務。這要取決于具體的選擇。例如,如果您計劃部署到僅具有 Web 容器的 Tomcat Web Server 上,則應該選擇創(chuàng)建 Web 應用程序,而不是 EJB 模塊。
將在“項目”窗口中顯示新建的 Web 服務。例如,對于 Web 應用程序,現(xiàn)在“項目”窗口將如下所示:
IDE 將自動創(chuàng)建服務器所需的部署描述符(如果有)。對于 Sun Java System Application Server,則不需要部署描述符。對于部署到 Tomcat Web Server 的 Web 服務,將添加 sun-jaxws.xml 以及 web.xml 中的 WSServlet 項。
在本練習中,您創(chuàng)建了一個 NetBeans 項目并建立了 Web 服務。
本練習的目的是對 IDE 生成的文件和代碼執(zhí)行一些有意義的操作。您將添加一個操作,該操作將從客戶端接收到的兩個數(shù)字相加起來。
@WebMethod public int add(@WebParam(name = "i") int i, @WebParam(name = "j") int j) { // TODO implement operation return 0; }
@WebMethod public int add(@WebParam(name = "i") int i, @WebParam(name = "j") int j) { int k = i + j; return k; }
在本練習中,您已為 Web 服務添加了代碼。
在將 Web 服務部署到 Web 容器時,IDE 允許您測試 Web 服務以查看它是否能夠按預期的那樣工作。鑒于此目的,我們在 IDE 中集成了 Sun Java System Application Server 提供的 Tester 應用程序。對于 Tomcat Web Server,也存在類似的工具。但是,Sun Java System Application Server 的 Tester 頁允許您輸入值并對其進行測試,而 Tomcat Web Server 則不允許這樣做。在后一種情況下,您只能看到已部署了 Web 服務,但是不能對值進行測試。目前還沒有用于測試 EJB 模塊是否已成功部署的工具。
測試是否已成功部署到 Web 容器:
注意:由于部署的 EJB 模塊的結果不會顯示在瀏覽器中,因此,如果您使用的是 EJB 模塊,則不能執(zhí)行上述步驟。
IDE 將啟動應用服務器、生成應用程序,并在瀏覽器中打開 tester 頁(如果已將 Web 應用程序部署到 Sun Java System Application Server)。對于使用 Tomcat Web Server 和部署 EJB 模塊來說,情況則有所不同:
Deployment of application CalculatorWSApplication completed successfully Enable of CalculatorWSApplication in target server completed successfully Enable of application in all targets completed successfully All operations completed successfully run-deploy: run: BUILD SUCCESSFUL
在本練習中,您已部署了 Web 服務并對其進行了測試。
部署 Web 服務后,我們需要創(chuàng)建一個客戶端來使用 Web 服務的 add 方法。在本練習中,我們將創(chuàng)建三個客戶端,即 Java SE 應用程序中的 Java 類、Web 應用程序中的 Servlet 和 JSP 頁。
注意:在編寫本文檔時,問題 10 還仍未得到解決,您必須在其路徑不包含空格的項目文件夾中創(chuàng)建 Web 服務客戶端。例如,路徑不能為 "C:\Documents and Settings\..."。
單擊“完成”。
將在“項目”窗口中顯示新建的 Web 服務客戶端:
System.out.println("Sum: " + port.add(3,4));
現(xiàn)在“輸出”窗口會顯示以下內(nèi)容:
compile: run: Sum: 7 BUILD SUCCESSFUL (total time: 1 second)
注意:在編寫本文檔時,問題 10 還仍未得到解決,您必須在其路徑不包含空格的項目文件夾中創(chuàng)建 Web 服務客戶端。例如,路徑不能為 "C:\Documents and Settings\..."。
單擊“完成”。
將在“項目”窗口中顯示新建的 Web 服務客戶端:
/* TODO output your page here
然后,刪除注釋掉代碼部分的結束行:
*/
在以下行之后添加一些空行:
out.println("<h1>Servlet ClientServlet at " + request.getContextPath () + "</h1>");
現(xiàn)在,在您添加的一個空行中單擊鼠標右鍵,選擇“Web 服務客戶端資源”>“調(diào)用 Web 服務操作”。將出現(xiàn)“選擇要調(diào)用的操作”對話框。
現(xiàn)在 processRequest 方法應該如下所示(下面以粗體顯示添加的代碼):
protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); out.println("<html>"); out.println("<head>"); out.println("<title>Servlet ClientServlet</title>"); out.println("</head>"); out.println("<body>"); out.println("<h1>Servlet ClientServlet at " + request.getContextPath () + "</h1>"); try { // Call Web Service Operation org.me.calculator.client.CalculatorWSService service = new org.me.calculator.client.CalculatorWSService(); org.me.calculator.client.CalculatorWS port = service.getCalculatorWSPort(); // TODO initialize WS operation arguments here int arg0 = 0; int arg1 = 0; // TODO process result here int result = port.add(arg0, arg1); System.out.println("Result = "+result); } catch (Exception ex) { // TODO handle custom exceptions here } out.println("</body>"); out.println("</html>"); out.close(); }
將 arg0 和 arg1 的值更改為其他數(shù)值,如 3 和 4。
將 System.out.println 語句更改為 out.println。
添加輸出異常的行(如果拋出異常)。
現(xiàn)在 try/catch 塊應該如下所示(會突出顯示新行和更改行):
try { // Call Web Service Operation org.me.calculator.client.CalculatorWSService service = new org.me.calculator.client.CalculatorWSService(); org.me.calculator.client.CalculatorWSApplication port = service.getCalculatorWSApplicationPort(); // TODO initialize WS operation arguments here int arg0 = 3; int arg1 = 4; // TODO process result here int result = port.add(arg0, arg1); out.println("<p>Result: " + result); } catch (Exception ex) { out.println("<p>Exception: " + ex); }
將啟動服務器(如果它尚未運行),生成并部署應用程序,并且打開瀏覽器以顯示計算結果。
注意:在編寫本文檔時,問題 10 還仍未得到解決,您必須在其路徑不包含空格的項目文件夾中創(chuàng)建 Web 服務客戶端。例如,路徑不能為 "C:\Documents and Settings\..."。
單擊“完成”。
將在“項目”窗口中顯示新建的 Web 服務客戶端。
將 arg0 和 arg1 的值更改為其他數(shù)值,如 3 和 4。
將啟動服務器(如果它尚未運行),生成并部署應用程序,并且打開瀏覽器以顯示計算結果:
有關使用 NetBeans IDE 5.5 開發(fā) Java EE 應用程序的更多信息,請參見以下資源:
要發(fā)送意見和建議、獲得支持以及隨時了解 NetBeans IDE Java EE 開發(fā)功能的最新開發(fā)情況,請加入 nbj2ee@netbeans.org 郵件列表。
本文檔介紹了有關使用 EJB 3.0 技術(作為 Java EE 5 平臺的一部分)開發(fā)企業(yè)應用程序的基礎知識,同時說明了 EJB 3.0 技術是如何簡化企業(yè)應用程序的開發(fā)過程的。本文檔使用的是 NetBeans IDE 5.5 發(fā)行版本。
預計持續(xù)時間:30 分鐘
本文檔假定您已具備了以下技術的一些基本知識或編程經(jīng)驗:
在學習本教程之前,您需要在計算機中安裝以下軟件:
在學習本教程之前,您需要在 IDE 中注冊 Sun Java System Application Server 的本地實例。
本練習的目的是:創(chuàng)建包含一個 EJB 模塊和一個 Web 模塊的 NewsApp 企業(yè)應用程序項目。NewsApp 應用程序使用消息驅(qū)動 Bean 接收和處理 Servlet 發(fā)送到隊列中的消息。該應用程序使用 Servlet 將消息發(fā)送到消息驅(qū)動 Bean 并顯示消息。
在本練習中,我們創(chuàng)建了包含一個 EJB 模塊和一個 Web 模塊的 Java EE 5 企業(yè)應用程序。
在本練習中,我們將在 EJB 模塊中創(chuàng)建對象。我們將創(chuàng)建實體類、消息驅(qū)動 Bean 和會話 Facade。我們還將創(chuàng)建一個持久性單元,以便為容器提供用于管理實體的信息,以及消息驅(qū)動 Bean 將使用的 Java 消息服務 (Java Message Service, JMS) 資源。
首先,我們將創(chuàng)建一個持久性單元,它用于定義在應用程序中使用的數(shù)據(jù)源和實體管理器。
單擊“完成”后,IDE 將創(chuàng)建 persistence.xml,并在源代碼編輯器中將其打開。關閉 persistence.xml。
在本練習中,我們將創(chuàng)建 NewsEntity 實體類。實體類是一個簡單的 Java 類。在創(chuàng)建實體類時,IDE 會添加 @Entity 標注以將該類定義為實體類。當創(chuàng)建了類后,我們將在該類中創(chuàng)建字段以表示表中所需的數(shù)據(jù)。
每個實體類都必須具有一個主鍵。在創(chuàng)建實體類時,IDE 會添加 @Id 標注以聲明要用作主鍵的字段。此外,IDE 還會添加 @Generated 標注以指定主 Id 的鍵生成策略。
要創(chuàng)建 NewsEntity 類,請執(zhí)行以下操作:
單擊“完成”后,將在源代碼編輯器中打開實體類 NewsEntity.java。在源代碼編輯器中,請執(zhí)行以下操作:
String title; String body;
在接下來的步驟中,我們將創(chuàng)建 NewMessage 消息驅(qū)動 Bean。
現(xiàn)在我們將在 EJB 模塊中創(chuàng)建 NewMessage 消息驅(qū)動 Bean。我們將使用“新建消息驅(qū)動 Bean”向?qū)韯?chuàng)建 Bean 和所需的 JMS 資源。
要創(chuàng)建 NewMessage 消息驅(qū)動 Bean,請執(zhí)行以下操作:
單擊“完成”后,將在源代碼編輯器中打開新建的消息驅(qū)動 Bean 類 NewMessage.java。您可以看到該類具有以下標注:
@MessageDriven(mappedName = "jms/NewMessage")
此標注向容器說明:該組件是一個消息驅(qū)動 Bean 并且該 Bean 使用 JMS 資源。當 IDE 生成類時,將從類 (NewMessage.java) 名稱派生資源的映射名 (jms/NewMessage)。JMS 資源被映射到目標的 JNDI 名稱,Bean 從該目標中接收消息。“新建消息驅(qū)動 Bean”向?qū)б褳槲覀儎?chuàng)建了 JMS 資源。通過 EJB 3.0 API,我們可以從 Bean 類中查找 JNDI 名稱空間中的對象,這樣就不需要配置部署描述符來指定 JMS 資源了。
EJB 3.0 規(guī)范允許您使用標注將資源直接引入類中。現(xiàn)在,我們準備使用標注將 MessageDrivenContext 資源引入類中,然后注入 PersistenceContext 資源,EntityManager API 將使用該資源來管理持久性實體實例。我們要在源代碼編輯器中將標注添加到類中。
public class NewMessage implements MessageListener { @Resource private MessageDrivenContext mdc;
@PersistenceContext private EntityManager em;并在代碼中生成以下方法:
public void persist(Object object) { // TODO: // em.persist(object); }
public void save(Object object) { em.persist(object); }
ObjectMessage msg = null; try { if (message instanceof ObjectMessage) { msg = (ObjectMessage) message; NewsEntity e = (NewsEntity) msg.getObject(); save(e); } } catch (JMSException e) { e.printStackTrace(); mdc.setRollbackOnly(); } catch (Throwable te) { te.printStackTrace(); }
接下來,我們將為 NewsEntity 實體類創(chuàng)建一個會話 Facade。要創(chuàng)建會話 Facade,請執(zhí)行以下操作:
單擊“完成”后,將創(chuàng)建會話 Facade 類 NewsEntityFacade.java,并在源代碼編輯器中將其打開。IDE 還將創(chuàng)建本地接口 NewsEntityFacadeLocal.java。
EJB 3.0 技術通過減少所需的代碼量來簡化會話 Bean 的創(chuàng)建。您可以看到,標注 @Stateless 用于將類聲明為無態(tài)會話 Bean 組件,并且該類不再需要實現(xiàn) javax.ejb.SessionBean 的語句。此外,代碼也更為簡潔了,因為利用 EJB 3.0 技術,業(yè)務方法不再需要使用代碼來聲明其拋出了所檢查到的異常。
您會看到,在創(chuàng)建會話 Facade 時,PersistenceContext 資源已直接注入到會話 Bean 組件中。
在本練習中,我們已對 EJB 模塊中的實體類和消息驅(qū)動 Bean 進行了編碼,然后為實體類創(chuàng)建了會話 Facade。此外,我們還創(chuàng)建了應用程序使用的 JMS 資源。
現(xiàn)在,我們將在 Web 模塊中創(chuàng)建 Servlet ListNews 和 PostMessage。這些 Servlet 將用于讀取和添加消息。
在本練習中,我們將創(chuàng)建一個用于顯示數(shù)據(jù)的簡單 Servlet。我們將使用標注從 Servlet 中調(diào)用實體 Bean。
單擊“完成”后,將在源代碼編輯器中打開類 ListNews.java。在源代碼編輯器中,請執(zhí)行以下操作:
out.println("<h1>Servlet ListNews at " + request.getContextPath () + "</h1>"); List news = newsEntityFacade.findAll(); for (Iterator it = news.iterator(); it.hasNext();) { NewsEntity elem = (NewsEntity) it.next(); out.println(" <b>"+elem.getTitle()+" </b><br />"); out.println(elem.getBody()+"<br /> "); } out.println("<a href='PostMessage'>Add new message</a>"); out.println("</body>");
在本練習中,我們將創(chuàng)建用于傳遞消息的 PostMessage Servlet。我們將使用標注將所創(chuàng)建的 JMS 資源直接注入 Servlet 中,并且指定變量名稱及其映射到的名稱。然后,添加用于發(fā)送 JMS 消息的代碼,以及用于在 HTML 表單中添加消息的代碼。
單擊“完成”后,將在源代碼編輯器中打開類 PostMessage.java。在源代碼編輯器中,請執(zhí)行以下操作:
public class PostMessage extends HttpServlet { @Resource(mappedName="jms/NewMessageFactory") private ConnectionFactory connectionFactory; @Resource(mappedName="jms/NewMessage") private Queue queue;
response.setContentType("text/html;charset=UTF-8"); // Add the following code to send the JMS message String title=request.getParameter("title"); String body=request.getParameter("body"); if ((title!=null) && (body!=null)) { try { Connection connection = connectionFactory.createConnection(); Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); MessageProducer messageProducer = session.createProducer(queue); ObjectMessage message = session.createObjectMessage(); // here we create NewsEntity, that will be sent in JMS message NewsEntity e = new NewsEntity(); e.setTitle(title); e.setBody(body); message.setObject(e); messageProducer.send(message); messageProducer.close(); connection.close(); response.sendRedirect("ListNews"); } catch (JMSException ex) { ex.printStackTrace(); } } PrintWriter out = response.getWriter();
out.println("Servlet PostMessage at " + request.getContextPath() + "</h1>"); // Add the following code to add the form to the web page out.println("<form>"); out.println("Title: <input type='text' name='title'><br/>"); out.println("Message: <textarea name='body'></textarea><br/>"); out.println("<input type='submit'><br/>"); out.println("</form>"); out.println("</body>");
現(xiàn)在可以運行項目了。在運行項目時,我們希望瀏覽器打開包含 ListNews Servlet 的頁面。可以通過在企業(yè)應用程序的“屬性”對話框中指定該頁的 URL 來實現(xiàn)這一目的。該 URL 是應用程序的上下文路徑的相對 URL。輸入相對 URL 后,可以從“項目”窗口中生成、部署并運行應用程序。
要設置相對 URL 并運行應用程序,請執(zhí)行以下操作:
運行項目時,將在瀏覽器中打開 ListNews Servlet,該 Servlet 用于顯示數(shù)據(jù)庫中的消息列表。如果您是第一次運行項目,則數(shù)據(jù)庫為空,但是您可以單擊“添加消息”來添加消息。
當您使用 PostMessage Servlet 添加消息時,消息會發(fā)送到消息驅(qū)動 Bean 以寫入到持久性存儲中,并會調(diào)用 ListNews Servlet 來顯示數(shù)據(jù)庫中的消息。由 ListNews 檢索的數(shù)據(jù)庫中的消息列表通常不包含新消息,這是因為消息服務是異步的。
下面是您創(chuàng)建項目時可能會遇到的一些問題。
使用向?qū)韯?chuàng)建 JMS 資源時,您可能會在輸出窗口中看到以下服務器錯誤消息:
[com.sun.enterprise.connectors.ConnectorRuntimeException: JMS resource not created : jms/Queue]
此消息可能表明沒有創(chuàng)建 JMS 資源,或者沒有在應用服務器中注冊該資源。您可以使用 Sun Java System Application Server 管理控制臺來檢查、創(chuàng)建以及編輯 JMS 資源。
要打開管理控制臺,請執(zhí)行以下操作:
您需要確保將 PostMessage Servlet 中的 JMS 連接工廠資源映射到在 Sun Java System Application Server 中注冊的 JMS 連接工廠資源的對應 JNDI 名稱上。
應在 Sun Java System Application Server 中注冊以下資源:
有關使用 NetBeans IDE 5.5 開發(fā) Java EE 應用程序的更多信息,請參見以下資源:
您可以在 Java EE 5 教程中找到有關使用 EJB 3.0 Enterprise Beans 的詳細信息。
要發(fā)送意見和建議、獲得支持以及隨時了解 NetBeans IDE Java EE 開發(fā)功能的最新開發(fā)情況,請加入 nbj2ee 郵件列表。
本文檔向您介紹了有關使用 Java™ 持久性開發(fā) Web 應用程序的基礎知識。Java 持久性 API 是作為 Java EE 5 平臺的一部分引入的。在本教程中,您將創(chuàng)建一個簡單的 Web 應用程序項目。我們將在項目中使用持久性,但是 Java 持久性 API 允許我們在無需創(chuàng)建 EJB 模塊的情況下使用持久性。
在項目中使用 Java 持久性可以大大簡化應用程序的開發(fā),因為您不需要配置部署描述符為持久性字段或?qū)傩蕴峁ο箨P系映射信息,而可以使用標注直接在簡單的 Java 類中定義這些屬性。
實體持久性是通過 EntityManager API 來管理的。EntityManager API 用于處理持久性上下文,而每個持久性上下文是一組實體實例。在開發(fā)應用程序時,您可以在類中使用標注來指定實體實例的持久性上下文實例,然后通過容器處理實體實例的生命周期。
本文檔使用的是 NetBeans IDE 5.5 發(fā)行版本。
預計持續(xù)時間:15 分鐘
本文檔假定您已具備以下技術的一些基本知識或編程經(jīng)驗:
在學習本教程之前,您需要在計算機中安裝以下軟件:
在學習本教程之前,您需要在 IDE 中注冊 Sun Java System Application Server 的本地實例。
本練習的目的是創(chuàng)建 ZooApp Web 應用程序項目。此應用程序?qū)⒗眯略龅?Java 持久性模型的功能。其中一項功能就是,實體對象可以成為簡單的 Java 類,因此不再需要將它們放置在 EJB 模塊中并將其打包到 EAR 文件中。這意味著,我們可以直接在 Web 應用程序內(nèi)創(chuàng)建實體類。
在本練習中,您創(chuàng)建了一個將包含實體類的 Java EE 5 Web 應用程序。
在本練習中,我們將創(chuàng)建一個持久性單元,目的在于通知容器哪些實體類需要由實體管理器進行管理,以及這些實體需要使用哪些數(shù)據(jù)源信息。
我們通過在 persistence.xml(將在 Web 模塊中創(chuàng)建此文件)中定義持久性單元的屬性來創(chuàng)建持久性單元。指定持久性單元的名稱后,將指定持久性提供程序,它包含容器將用于管理實體實例的 API。此外,還需要指定數(shù)據(jù)源和表生成策略。我們將使用“新建持久性單元”向?qū)?chuàng)建持久性單元。
注意:也可以在“新建 - 實體類”向?qū)е袆?chuàng)建持久性單元。創(chuàng)建實體類時,如果不存在持久性單元,該向?qū)⑻崾疚覀冞M行創(chuàng)建。
缺省提供程序是 TopLink Essential.jar。TopLink Essential.jar 包含了 Java 持久性的庫。并且實體管理器位于 TopLink Essential.jar 中。
缺省數(shù)據(jù)源 jdbc/sample 用于連接到與 Sun Java System Application Server 捆綁在一起的 Java DB 數(shù)據(jù)庫。
單擊“完成”后,IDE 將創(chuàng)建 persistence.xml,并在源代碼編輯器中將其打開。通過單擊源代碼編輯器工具欄中的 "XML",可以查看 persistence.xml 的 XML。此文件包含了 Java EE 5 容器管理應用程序的實體和持久性所需的所有信息。
在本練習中,您創(chuàng)建了一個持久性單元,用來指定數(shù)據(jù)源、要永久保存的實體類以及容器將用于管理實體生命周期的實體管理器。
在本練習中,我們將創(chuàng)建兩個實體類,即 Animal.java 和 Pavilion.java,它們用于表示要創(chuàng)建的關系數(shù)據(jù)庫中的表。此外,還將在類中定義一些字段來表示數(shù)據(jù)。Java 持久性規(guī)范允許我們使用標注為容器提供有關字段的信息,如對象關系映射信息。
首先,我們將創(chuàng)建實體類 Animal。此類用于表示數(shù)據(jù)庫中的 ANIMAL 表。在創(chuàng)建實體類時,IDE 會添加 @Entity 標注以將該類定義為實體類。創(chuàng)建類后,我們將在類中創(chuàng)建字段以表示表中所需的數(shù)據(jù),并使用標注提供有關一些字段的其他信息。
每個實體類都必須具有一個主鍵。在創(chuàng)建實體類時,IDE 會添加 @Id 標注以聲明要用作主鍵的字段。此外,IDE 還會添加 @Generated 標注以指定主 Id 的鍵生成策略。
要創(chuàng)建 Animal 類,請執(zhí)行以下操作:
單擊“完成”后,將在源代碼編輯器中打開新建的實體類 Animal.java。在源代碼編輯器中,請執(zhí)行以下操作:
String name; String kind; String weight; Pavilion pavilion;
@Column(name="animalName")
@ManyToOne
在下一步中,我們將創(chuàng)建 Pavilion 實體類。
現(xiàn)在我們將創(chuàng)建實體類 Pavilion,它用于表示數(shù)據(jù)庫中的 PAVILION 表。我們將再次在類中使用標注指定一些字段的對象關系映射。要創(chuàng)建 Pavilion 類,請執(zhí)行以下操作:
單擊“完成”后,將在源代碼編輯器中打開新建的實體類 Pavilion.java。在源代碼編輯器中,請執(zhí)行以下操作:
String name; String address; Collection <Animal> animals;
@Column(name="pavilionName")
@OneToMany(mappedBy="pavilion")
在本練習中,您創(chuàng)建了兩個實體類并定義了字段,而且還使用標注定義了在部署應用程序時將在表中生成的某些列的屬性。
現(xiàn)在我們希望創(chuàng)建一些簡單的 Web 頁,以查看是否創(chuàng)建了數(shù)據(jù)庫表以及是否可以添加數(shù)據(jù)。我們將為應用程序添加 Java Server Faces (JSF) 頁,并使用“通過實體類創(chuàng)建 JSF 頁”向?qū)Э焖賱?chuàng)建一個簡單的 Web 接口。
單擊“完成”后,IDE 將生成所需的 JavaServer Faces 文件,以便我們可以運行并測試 ZooApp。
在本練習中,我們將部署 ZooApp Web 應用程序項目并測試該應用程序。
單擊“運行項目”后,將在瀏覽器中打開一個頁面,您可以通過該頁面包含的菜單,查看場館 (Pavilions) 和動物 (Animals) 的列表。
您還可以添加、編輯或刪除動物 (Animals) 和場館 (Pavilions) 數(shù)據(jù)。
在本練習中,您生成了為 Java EE 5 平臺開發(fā)的 ZooApp Web 應用程序,然后部署并測試了該 Web 應用程序。
下面是您創(chuàng)建項目時可能會遇到的一些問題。
使用該向?qū)ㄟ^實體類創(chuàng)建 JSF 頁時,您可能會在向?qū)е锌吹揭韵洛e誤消息:
"This wizard can only be used in a web project with JSF support."(此向?qū)е荒茉诰哂?JSF 支持的 Web 項目中使用。)
如果看到此消息,則需要檢查是否已將 Java Server Faces 框架添加到項目屬性中。通過執(zhí)行以下操作,可以將 Java Server Faces 支持添加到 Web 項目中:
將 JSF 框架添加到項目屬性后,您應該能夠使用該向?qū)?chuàng)建 JSF 頁了。
有關使用 NetBeans IDE 5.5 開發(fā) Java EE 應用程序的更多信息,請參見以下資源:
您可以在 Java EE 5 教程中找到有關使用 Java 持久性的詳細信息。
要發(fā)送意見和建議、獲得支持以及隨時了解 NetBeans IDE Java EE 開發(fā)功能的最新開發(fā)情況,請加入 nbj2ee 郵件列表。
Java EE 5 教程
|
NetBeans IDE 5.5 的開發(fā)是在 Java EE 和 Glassfish 工作組的密切合作下完成的,它實現(xiàn)了與新的 Java EE 5 規(guī)范的完美集成,從而可以在此 IDE 中輕松地使用新規(guī)范。NetBeans IDE 5.5 是您快速學習 Java EE 5 編程并使用它高效工作的最佳途徑。
本文檔介紹了 Java EE 5 規(guī)范的一些重要概念,以及它們?nèi)绾闻c您目前的編程項目相關聯(lián)的。下面將介紹以下主題:
您可以在 Java EE 5 教程中找到有關開發(fā) Java EE 5 應用程序并在 Sun Java System Application Server Platform Edition 9 上部署該應用程序的信息。
Java EE 5 平臺不需要任何部署描述符(Servlet 規(guī)范所需的部署描述符 web.xml 文件除外),從而簡化了部署過程。其他部署描述符(如 ejb-jar.xml 以及在 web.xml 中與 Web 服務相關的條目)已過時。J2EE 1.4 部署描述符通常很復雜,在填充它們時很容易出錯。但是,Java EE 5 平臺使用的是“標注”。標注是 Java 修飾符,與代碼中指定的 public 和 private 類似。例如,EJB 3 規(guī)范(Java EE 5 規(guī)范的子集)為 Bean 類型、接口類型、資源引用、事務屬性、安全性等定義了標注。JAX-WS 2.0 規(guī)范為 Web 服務提供了一組類似的標注。有些標注用來生成工件,另外一些標注用來描述代碼,還有一些標注用來提供增強的服務,如安全性或特定于運行時的邏輯。總之,Java EE 5 平臺為以下任務(以及其他任務)提供了標注:
標注使用 @ 字符來標記。在 IDE 中,當您創(chuàng)建了使用 Java EE 5 中的標注的類型時,將在生成的代碼中提供相關的占位符。例如,當您使用 IDE 創(chuàng)建無態(tài)會話 Bean 時,將生成以下代碼,其中包括 @Stateless() 標注:
package mypackage; import javax.ejb.*; @Stateless() public class HelloWorldSessionBean implements mypackage.HelloWorldSessionLocal { }通過代碼完成,您可以對特定于光標下的項的標注屬性進行訪問。例如,在 @WebService() 標注的括號中按 Ctrl-空格鍵時,將看到以下內(nèi)容:
每個屬性都具有缺省值。因此,除非您需要使用缺省值以外的其他值,否則不必指定任何屬性。在一些簡單的示例中,使用缺省值就可以了,這意味著您根本不需要提供屬性。
使用新的 EJB 3.0 API,可以減少并簡化開發(fā)者需要完成的工作量,從而可以更輕松地進行軟件開發(fā)。換句話說,就是使用了更少的類和代碼。這是因為現(xiàn)在容器承擔了更多的工作,從而實現(xiàn)了這一目的。下面是新 EJB 3.0 API 的一些功能和優(yōu)點:
EJBContext
直接在類中查找 JNDI 名稱空間中的對象。
在 IDE 中,您可以對 Enterprise Beans 進行編碼,就像對其他 Java 類進行編碼一樣,方法是:使用代碼完成和編輯器提示實現(xiàn)正確的方法并使類與其接口保持同步。您不必使用特殊命令和對話框生成諸如業(yè)務方法或 Web 服務操作之類的內(nèi)容,雖然這些命令仍可以幫助您熟悉 Java EE 5 代碼的語法。
要請求資源注入,組件需要使用 @Resource
標注或針對一些專用資源的 @EJB
和 @WebServiceRef
標注。您可以注入以下資源:
SessionContext
對象
DataSources
對象
EntityManager
接口
在 IDE 中,源代碼編輯器為 Java EE 5 平臺提供的資源注入標注提供了完整的代碼完成。此外,當您運行諸如“調(diào)用 EJB”和“使用數(shù)據(jù)庫”之類的命令時,IDE 會自動將資源注入到文件中。
Java EE 5 平臺引入了新的 Java 持久性 API(它是作為 JSR-220 的一部分開發(fā)的)。Java 持久性 API 不但可以在 EJB 組件外部使用(例如,在 Web 應用程序和應用程序客戶端中使用),而且還可以在 Java EE 平臺之外的 Java SE 應用程序中使用。
Java 持久性 API 具有以下主要功能:
JAR
、應用程序客戶端 JAR
、WEB-INF/lib
、WEB-INF/classes
的一部分,甚至是企業(yè)應用程序歸檔 (Enterprise Application Archive, EAR) 文件中實用程序 JAR
的一部分。通過這些簡單的打包規(guī)則,您不再需要創(chuàng)建 EAR 文件以使用來自 Web 應用程序或應用程序客戶端的實體 Bean。
創(chuàng)建、讀取、更新和刪除
(Create Read Update Delete, CRUD) 操作。 IDE 提供了處理新 Java 持久性 API 的工具。您可以通過數(shù)據(jù)庫自動生成實體類,或手動對實體類進行編碼。IDE 還提供了用于創(chuàng)建和維護持久性單元的模板和圖形編輯器。有關使用新 Java 持久性 API 的詳細信息,請參見 Java EE 5 平臺中的 Java 持久性。
在 Java EE 5 平臺中,通過使用標注顯著改進和簡化了 Web 服務支持。以下規(guī)范已應用于此領域中:JSR 224,Java API for XML-Based Web Services (JAX-WS) 2.0;JSR 222,Java Architecture for XML Binding (JAXB) 2.0 以及 JSR 181,Web Services Metadata for the Java Platform。
JAX-WS 2.0 是 Java EE 5 平臺中用于 Web 服務的新 API。作為 JAX-RPC 1.1 的后繼者,JAX-WS 2.0 保留了自然的 RPC 編程模型,同時在以下幾個方面進行了改進:數(shù)據(jù)綁定、協(xié)議和傳輸?shù)莫毩⑿浴?Web 服務的 REST
樣式的支持以及易開發(fā)性。
與 JAX-RPC 1.1 的主要區(qū)別在于,現(xiàn)在所有數(shù)據(jù)綁定都委托給了 JAXB 2.0。這樣,基于 JAX-WS 的 Web 服務就可以使用百分之百的 XML 架構,從而提高了互操作性和易使用性。這兩種技術已很好地集成在一起了,因此用戶不必再為使用兩套不同的工具而勉為其難了。從 Java 技術類開始開發(fā)時,JAXB 2.0 可以生成自動嵌入到 Web 服務描述語言 (Web Service Description Language, WSDL) 文檔內(nèi)的 XML 架構文檔,從而用戶無需手動執(zhí)行這一容易出錯的集成。
現(xiàn)成的 JAX-WS 2.0 本身可以支持 SOAP 1.1、SOAP 1.2 和 XML/HTTP 協(xié)議。協(xié)議可擴展性從一開始就是人們所希望實現(xiàn)的目標,JAX-WS 2.0 允許供應商支持其他協(xié)議和編碼來獲取更佳的性能(例如 FAST Infoset)或?qū)S玫膽贸绦颉eb 服務(使用附件來優(yōu)化大型二進制數(shù)據(jù)的發(fā)送和接收)可以利用 W3C 制訂的 MTOM/XOP(“消息傳輸優(yōu)化機制/XML 二進制優(yōu)化打包”的簡稱)標準,且對編程模型沒有任何不利的影響(有關 MTOM/XOP 的信息,請參見此頁)。在 Java EE 5 技術出現(xiàn)之前,定義 Web 服務需要使用很長且復雜的描述符。現(xiàn)在,定義它卻非常簡單,只需將 @WebService
標注放置在 Java 技術類上即可。類上的所有公共方法會自動發(fā)布為 Web 服務操作,并且其所有參數(shù)都將使用 JAXB 2.0 映射到 XML 架構數(shù)據(jù)類型。
由于 Web 服務調(diào)用是通過網(wǎng)絡來實現(xiàn)的,因此這種調(diào)用所花費的時間是無法預測的。許多客戶端(特別是交互式客戶端,如基于 JFC/Swing 的桌面應用程序)由于必須等待服務器的響應而嚴重地影響了它們的性能。為了避免出現(xiàn)性能降低,JAX-WS 2.0 提供了新的異步客戶端 API。使用此 API,應用程序編程人員不再需要自己創(chuàng)建線程,而是依賴于 JAX-WS 運行時為他們管理長時間運行的遠程調(diào)用。
異步方法可以與 WSDL 生成的任何接口以及動態(tài)性更強的 Dispatch
API 聯(lián)合使用。為了方便起見,在導入 WSDL 文檔時,您可以要求為 Web 服務定義的任何操作生成異步方法。
以下提供了兩種使用模型:
請注意,異步調(diào)用支持是完全在客戶端上實現(xiàn)的,因此不需要對目標 Web 服務進行任何更改。
IDE 提供了處理 JAX-WS 的工具。您可以使用“新建文件”向?qū)е械哪0迳?JAX-WS 工件。異步 Web 服務可以通過 Web 服務定制編輯器來創(chuàng)建。代碼完成功能包括可以在 Web 服務中使用的標注。
有關使用 NetBeans IDE 5.5 開發(fā) Java EE 應用程序的更多信息,請參見以下資源:
要發(fā)送意見和建議、獲得支持以及隨時了解 NetBeans IDE Java EE 開發(fā)功能的最新開發(fā)情況,請加入 nbj2ee@netbeans.org 郵件列表。