 |
Introduction to using Java Persistence API in a web application in Java EE environment
Posted by ss141213 on December 04, 2005 at 03:29 PM | Comments (27)
The Java Persistence API is the standard API for the management of persistence and object/relational mapping in Java EE 5 platform. Every Java EE 5 compatible application server will support this API. In this entry I will show how to use this API from a web application in Java EE environment.
Since the public review version of Java Persistence API specification, there have been a number of significant changes about packaging of applications that use Java Persistence API. So I will also show how to package such an application in a portable way. In this exercise, I will not use any kind of IDE because I don't want any magic! This article is going to be a long one because I am trying to explain the steps in detail as well as why certain things are done the way they are. But the steps are very simple as you can see from the README.
What is the example?
It's a web application which has a login page and a new user registration page. It talks to a database where user details are stored. The complete sample is available here. There is a README inside that zip file as well.
Requirements
The Reference Implementation(RI) of the Java Persistence API is being done in glassfish project. You can download the latest promoted build and try out the sample yourself.
Although I am using the RI to build and run this sample, atmost you have to do some very minor changes to persistence.xml and build.xml to make this app build and run in any other application server that supports Java EE 5 specification.
Detailed Steps
Step #1: Write entity bean UserCredential.java
Points to note about this entity bean are:
1) This is a Plain Old Java Object (POJO): It does not extend any predefined class, nor does it implement any particular interface other than java.io.Serializable. An entity bean must implement java.io.Serializable interface if it is used in RMI-IIOP interface. Since in this example, RMI-IIOP is not used, technically this bean does not have to implement this interface. Never-the-less it is a good idea to make an entity bean RMI-IIOP ready, so I have done so.
2) There is no deployment descriptor needed to specify that it is an entity bean. Instead the class has been annotated as @Entity as shown below:
@Entity
public class UserCredential implements java.io.Serializable.
The persistence provider automatically determines whether we are annotating FIELDs or PROPERTIEs. In this case, we have decided to annotate fields.
3) Every entity bean must have an identity. In our case, it is specified using @Id as below:
@Id private String name;
I have chosen to use @Id because we have a single field primary key. Other annotations like @IdClass, @EmbeddedId are typcally used for composite primary key.
4) Also note that we have not used @GeneratedValue along with @Id. When an id field is not annotated with @GeneratedValue, it means that user is responsible for setting the identity. Provider will not set the id field.
5) Although it is possible to specify exact table name and column names for the entity bean, we can rely on the default mapping that the specification defines. Because of the default mapping rules, UserCredential bean gets mapped to a table called USERCREDENTIAL, name & password fiels get mapped to NAME and PASSWORD columns respectively.
Step #2: Define a persistence unit
A Persistence Unit (PU) is a logical grouping of a set of related entity beans. A PU also contains configuration details about the entity managers that are going to manage these entity beans. Actually the ocnfiguration is applied to an EntityManagerFactory which in turn creates homogenious entity manager instances. To define a PU we need to write a node in persistence.xml file. Points to note about this persistence.xml are:
1) One persistence.xml can be used to define multiple PUs, but in this case we have defined only one PU by name em1.
2) We need not specify any other elements/attributes, as the default values are just fine for most applications. e.g. by default the entity manager's transaction type is JTA.
3) There is no need to enumerate all the entity bean class names inside because we are defining only one PU in this persistence.xml and we will be packaging the entity classes along with this persistence.xml in a jar file, so container can discover all the entity beans.
Step #3: Write LoginServlet.java
Points to note about this servlet are:
1) It talks to database using EntityManager API. It declares dependency on an EntityManagerFactory using @PersistenceUnit annotation:
@PersistenceUnit private EntityManagerFactory emf;
Since there is only one Persistence Unit(PU) defined in the scope of the web-app, there is no need to specify the unitName in @PersistenceUnit. Please also note that an injected variable in a servlet or ejb must not be declared static or final. In our example, emf follows this rule as well.
2) Also note that the servlet does not have an instance field of type EntityManager. This is because EntityManager is not thread safe. Since this servlet is not denoted as a SingleThreadModel servlet, one instance of servlet gets shared by mutiple clients and service method of servlet can be called by multiple threads concurrently. So we can't directly inject an EntityManager. Instead we inject an EntityManagerFactory which is thread safe.
3) In the service(), we create an EntityManager using the following code:
EntityManager em = emf.createEntityManager();
We also close the EntityManager in the finally block.
4) When user tries to login using a user name and password, it uses
UserCredential credential = em.find(UserCredential.class, name);
to find a matching UserCredential entity in the database.
5) Since EntityManager.find does not require a transaction to be started, the servlet does not have to begin a transaction before calling em.find().
Step #4: Write RegistrationServlet.java
Note:
1) It uses an injected EntityManagerFactory.
2) It uses
EntityManager.persist()
to create a new entity in the in the database .
3) Since EntityManager.persist() needs to be called in the context of a transaction, this servlet begins a transaction by calling
utx.begin()
and commits the tx before returning.
4) Also note that it uses an injected UserTransaction object as follows:
@Resource private UserTransaction utx;
Unlike EntityManager, UserTransaction is thread safe, so it is OK to inject it into a servlet.
5) Also see how we ensure that when we close the EntityManager there is no active transaction context, otherwise EntityManager.close() will throw an exception.
Step #5: Write web.xml
We are having to write an web.xml only because we have to define a couple of request path mappings to servlets.
Step #6: Write a couple of html files
Refer to login.html and registration.html inside the sample zip file. They are used to call the servlets.
Step #7: Build using build.xml
This is a very simple build.xml just to demonstrate the compilation and packaging process.
The build targets are:
build -- This builds the war file.
clean -- cleans
verify -- verify uses a tool called verifier that checks compliance of the application against Java EE spec.
deploy -- deploys the war file
undeploy -- undeploys the war file
The last three targets are specific to Java EE 5 Reference Implementation (a.k.a. Sun Java System Application Server 9 PE).
As you can see, to compile only library needed is javaee.jar which contains the Java EE 5 platform APIs.
Packaging is done in two steps:
a) build-entities target which compiles only entity beans, copies persistence.xml to output directory and make a jar file called entities.jar. See entities.jar contains persistence.xml in META-INF dir. This jar file is used during compilation of servlets. More over this is also bundled inside the war file's WEB-INF/lib directory.
b) build-web-app1 target which compiles the servlets, copies web.xml and html files to output directories amd makes a war file called web-app1.war. A few points worth noting here are:
in addition to javaee.jar, entities.jar is also used while compiling the servlets.
servlet classes are bundled in WEB-INF/classes directory.
entities.jar is bundled inside WEB-INF/lib directory
Another packaging option is to package META-INF/persistence.xml and entity classes in WEB-INF/classes. But I just feel having a separate jar file with entities in it keeps things clean. It improves reusability.
Step #8: Set up a data source
By default entity manager uses the default pre-configured data source with JNDI name jdbc/__default that glassfish comes with. This data source talks to a Derby database called sun-appserv-samples. Refer to the README where I have listed the command needed to create the tables in Derby. Glassfish has a feature called Java2DB which can autocreate the database schema during deployment, but because of a bug this feature is not currently supported for Derby. Very soon (in a week or so), this bug is going to be fixed. Watch out glassfish EJB 3.0 persistence project page.
Step #9: Run the web-app
Type http://localhost:8080/web-app1/login.html in the browser. Replace localhost & 8080 by host and port as appropriate in your env.
Hope this entry is useful. In the next entry, we will take a step further and write an enterprise application where a web application talks to database using session beans.
Technorati Tags: glassfish persistence
Bookmark blog post: del.icio.us Digg DZone Furl Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment
-
Being the typical developer, I'm short on time. So, having glassfish and postgres preinstalled, I tried to deploy the prebuilt war directly and see if it works. I created the proper pool in GlassFish, changed the jdbc/__default resource to use the blog_1 pool, and added the table to the database.
However, when I try to run the html and register a new user, I get this error:
javax.servlet.ServletException: java.lang.IllegalStateException: Unable to retrieve EntityManagerFactory for unitName null
In the post, you mention that Since there is only one Persistence Unit(PU) defined in the scope of the web-app, there is no need to specify the unitName in @PersistenceContext. Is this correct? It appears the error I'm getting in glassfish is related to this.
I'm not sure which version of GF I'm running -- it could be as old as a couple of months. -- Richard
Posted by: rbair on December 05, 2005 at 10:15 AM
-
First and foremost, I appreciate you for trying out the sample. Back to problems you are facing:
Yes, it is correct that when there is only one PU in the context of a component, then there is no need to specify the unitName. Here are a few things I can think of:
1) In last couple of months, there have been a lot of significant changes in the glassfish. So is there any chance of you upgrading to the latest build (you can find a pointer in my blog entry) and retrying?
2) Are you building the app or using the prebuilt war file? Can you please try running 'ant verify' and see if verifier detects any issues?
3) Do you see any issues in the server log($GLASSFISH_HOME/domains/domain1/logs/server.log)? Look for a message like "PersistenceInfo for this pud is" and see if you see any useful message that follow that. Would you mind forwarding me the server.log?
I suggest you upgrade to a more recent build and try the sample. -- Sahoo
Posted by: ss141213 on December 05, 2005 at 11:03 AM
-
It looks well. I have used Hibernate 3 EntityManager in a WEB application, which also support using the JEE Persistence API, but i still need lookup the EntityManager in a servlet.
Does the Servlet's Injection require new Servlet Specification? Which version spec define the injection of servlet? Thanks
Posted by: wangzaixiang on December 05, 2005 at 05:46 PM
-
I have tried a similar demo from the one you showed. I substituted derby for mysql. Under derby the application works fine, but under mysql I receive an error message which is below. Do you know what may cause this error? I feel I need to add something to persistence.xml, but I don't know what. Would you be able to help me?
org.apache.jasper.JasperException: Unable to compile class for JSP
Generated servlet error:
[javac] C:\Sun\glassfish\domains\domain1\generated\jsp\j2ee-modules\Javaee5_Tut\org\apache\jsp\welcomeJSF_jsp.java:122: cannot find symbol
[javac] symbol : method setJspId(java.lang.String)
[javac] location: class com.sun.faces.taglib.jsf_core.ViewTag
[javac] _jspx_th_f_view_0.setJspId("id14");
[javac] ^
Generated servlet error:
[javac] C:\Sun\glassfish\domains\domain1\generated\jsp\j2ee-modules\Javaee5_Tut\org\apache\jsp\welcomeJSF_jsp.java:160: cannot find symbol
[javac] symbol : method setJspId(java.lang.String)
[javac] location: class com.sun.faces.taglib.html_basic.OutputTextTag
[javac] _jspx_th_h_outputText_0.setJspId("id17");
[javac]
Posted by: udarnick on December 05, 2005 at 06:23 PM
-
You asked:
>It looks well. I have used Hibernate 3 EntityManager in a WEB application, which
also support using the JEE Persistence API, but i still need lookup the EntityManager in a servlet.
>Does the Servlet's Injection require new Servlet Specification? Which version spec define the injection of servlet?
Ans:
Yes it is a maintenance release to the servlet spec which defines annotations in servlets. It is called Servlet 2.5-MR and is a required part of Java EE 5 platform . Visit this.
Posted by: ss141213 on December 05, 2005 at 06:45 PM
-
You wrote:
> I have tried a similar demo from the one you showed. I substituted derby for mysql. Under derby the application works fine, but under mysql I receive an error message which is below. Do you know what may cause this error? I feel I need to add something to persistence.xml, but I don't know what. Would you be able to help me?
Ans:
Yes, at this point replacing Derby by another database may require a different property to be used in persistence.xml, but you are not facing this. I am saying this because:
replacing derby with mysql can not cause JSP compilation problems. So I suggest you try verifying the application as it will tell you if there are any issues with your JSPs. It looks like some tag libraries are not correctly packaged. To verify, run the following command:
%GLASSFISH_HOME%\bin\verifier.bat "path to Javaee5_Tut.war"
This prodces a report and see what that tells. If you have issues, please post an issue at our web-app developer forum.
-- Sahoo
Posted by: ss141213 on December 05, 2005 at 07:00 PM
-
just curious - what the setup may look like to run this outside of the servlet container? For example, for Unit tests - as I can easily do the same in Spring/Hibernate (even with hibernate3/annotations)
Posted by: mgarber on December 06, 2005 at 09:16 AM
-
It is a spec requirement that Persistence API be usable in JavaSE environment. So glassfish project also supports this. Refer to using Java Persistence API in Java SE.
Keeping this in mind, in my blog I mentioned that persistence entities should be packaged in their own jar file so that they can be tested separately. glassfish also comes with a tool called verifier that does static analysis of entity beans and figures out common mistakes like missing @Id etc.
Sahoo
Posted by: ss141213 on December 06, 2005 at 09:36 AM
-
Sahoo,
could you also explain where you specify under what dbs issolation level a query must be executed. Is for example with an annotation like: @isolation_level read_uncommited.
If not what else?
thank you in advance
Posted by: rbak on December 09, 2005 at 06:59 AM
-
Hi,
You asked:
"could you also explain where you specify under what dbs issolation level a query must be executed. Is for example with an annotation like: @isolation_level read_uncommited.
If not what else?"
By default query is executed using the isolation level as set while configuring the underlying datasource.
Unfortunately there is no annotation available to specify query isolation level. Even worse, there is no standard (or portable) way to specify query isolation level. Too bad, I must say.
The only API that is available now is "Query setHint(String hintName, Object value)" in javax.persistence.Query
As you can see, this is a very loose interface and the spec does not define any standard constants for isolation level etc. So it is provider specific. For the default persistence provider used in glassfish, it is called lockMode.
In final version of the spec, we are expecting the spec to allow users to specify hints in @NamedQuery and @NamedNativeQuery.
Hope this answsered your question.
-- Sahoo
Posted by: ss141213 on December 09, 2005 at 08:43 AM
-
The original sample attached in this blog entry had a bug. It used to inject an EntityManager in the servlet which was not declared single threaded. Given that EntityManager (or a PersistenceContext) is not thread safe object and such a servlet gets shared by multiple requests and hence its service() method can be called concurrently, I have now changed the example to use an injected EntityManagerFactory instead. Thanks to my colleague Pieere Deisle who pointed out the concurrency issue.
-- Sahoo
Posted by: ss141213 on December 19, 2005 at 11:13 AM
-
Hi Sahoo,
Note, there's a minor incosistency in your example. Your sample code uses:
// This injects the default entity manager factory
@PersistenceUnit
private EntityManagerFactory emf;
to inject the entity manager, while in the text above you correctly state to just use:
@PersistenceContext private EntityManager em;
Regards,
Brian
Posted by: bleonard on January 03, 2006 at 12:45 PM
-
Sorry, I just now read the comment below my latest on using the EntityMangerFactory. This approach seems to somewhat conflict the the spec however: "The application may also use the EntityManagerFactory.getEntityManager() method to obtain a container-managed entity manager. This method, however, is intended primarily for use by the container in Java EE environments."
Posted by: bleonard on January 03, 2006 at 12:50 PM
-
Hi Brian,
This is in reference to your latest comment where you mentioned why the app is using createEntityManager as opposed to getEntityManager.
You are right, currently code is using
EntityManagerFactory.createEntityManager()
to get hold of an EntityManager. Such an EntityManager is called Application Managed Entity Manager and application code is responsible to manage its life cycle.
The other option is to use
EntityManagerFactory.getEntityManager().
Such an entity manager is called Container Managed Entity Manager whose life cycle is managed by container, hence less coding to do.
There is not much of a difference in our application, because it does not rely on persistence context propagation. So either option works just fine for us.
The prposed final draft recommends Java EE applications to use container managed entity manager. How ever, I don't recollect earlier version of the spec to recommend such a thing. So the app is using Application Managed Entity Manager.
Thanks for reminding. I shall change the application to use latest spec recommendation. How ever in order to avoid confusion I shall do that in a new article retaining this sample as is. It can serve as one that shows how to use application managed entity manager. I shall write one more that shows how to use container managed entity manager. I shall link the new one from the old one appropriately.
Thanks,
Sahoo
Posted by: ss141213 on January 03, 2006 at 05:05 PM
-
Java Persistence API spec is still changing. My original posting was written before the proposed final draft spec came out. In the proposed final draft, there were quite a few changes and I have now updated this blog accordingly. The only change needed was removal of AccessMode from @Entity. Now this example works with latest build of GlassFish. I don't anticipate any further changes in the spec that can affect this example.
Secondly Java2DB is now supported in GlassFish. So if some one wants to see this in action refer to my another blog where I have used it.
Thanks, Sahoo
Posted by: ss141213 on February 03, 2006 at 08:16 AM
-
Hi Sahoo,
When i deploy your Sample to Glassfish build 36, I see the following error when I post a submit on the Registration Servlet:
java.lang.IllegalStateException: Unable to retrieve EntityManagerFactory for unitName null
....
javax.servlet.http.HttpServlet.service(HttpServlet.java:822)
Any idea what could be causing this issue?
Thanks
Prasanth
Posted by: prashi123 on February 05, 2006 at 01:07 AM
-
As you can see from my comments made on 3 Feb '06, I have recently updated the sources and binaries to be compatible with latest GlassFish build because of changes in EJB 3 spec. Before uploading the files, I had checked them myself and they were working. So I am suspecting some environment/set up issue here.
0) When did you download the sources/binary?
1) Did you build the app yourself or are you deploying the prebuilt web-app? Are you using the Ant script to build/deploy?
2) Did you see errors during deployment?
3) Do you see any related messages in the server log($GLASSFISH_HOME/domains/domain1/logs/server.log)? Look for a message like "PersistenceInfo for this pud is" and see if you see any useful message that follow that. Would you mind forwarding me the server.log in an email?
4) Was database started?
If the problem persists, then send me a separate email with all these answers and we shall see what the problems are.
-- Sahoo
Posted by: ss141213 on February 05, 2006 at 06:29 PM
-
I have recently updated the sample to be compatible with most recent GlassFish builds. Now that persistence spec is stable as well as GlassFish has gone past beta, I don't anticipate any changes to this sample. The recent changes included the following:
adding version attribute to persistence.xml and
using password file option in build.xml.
Thanks,
Sahoo
Posted by: ss141213 on March 23, 2006 at 06:35 AM
-
Because of a change in spec, it is now required that an application managed entity manager created before utx.begin() should also call EntityManager.joinTransaction(). Without this change, this app was not working in Hibernate-GlassFish combination. So, I have now made necessary change. It works both in TopLink and Hibernate. The difference is that I now create EM after utx.begin().
Sahoo
Posted by: ss141213 on March 30, 2006 at 12:53 AM
-
Hallo Sahoo,
I have a questions about the use of an entityManager in a web applications like yours.
My questions is, if I can use the call off
"em = emf.createEntityManager()" in different classes within one transaction or should I use only one and the same entity manager within one transaction?
For me it was not clear to see it from the example you provided!
Thanks in advance!
Uwe
Posted by: uwe_gerger on September 01, 2006 at 01:53 AM
-
One entity manager created using emf.createEntityManager can take part in *one* transaction at the *same* time. Don't attempt to share the same entity manager in multiple transaction. You can how ever pass the same em instance around and use it from multiple functions/classes as long as you are using it in the same transaction context.
Another point worth mentioning is that you should not use the same entity manager in multiple threads concurrently.
If you are writing a web application or EJB application, I suggest you use *container manager entity manager".
Sahoo
Posted by: ss141213 on September 03, 2006 at 08:19 PM
-
Thank you,
my question is *can* I pass the em as a parameter to the different functions/classe or is it *absolutely necessary/a condition* to pas the em to the different functions/classes that participate in the same transaction?
If not, does it mean, I can call in each function/method, which are used in the same transaction, em=emf.getEntityManager()?
Thanks
Uwe
Posted by: uwe_gerger on September 08, 2006 at 06:30 AM
-
Hi Uwe
I thought my last response was pretty clear, looks like I was wrong. Can you post your question in GlassFish forum with snippet of code that shows what you intend to do. I don't understand how you write you app such that different functions in the same request call stack take part in different transactions.
Sahoo
Posted by: ss141213 on September 13, 2006 at 12:02 AM
-
Hi, I use web start for my application. My question is how to use EntityManagerFactory. EntityManagerFactory emf = Persistence.createEntityManagerFactory("PU"); gives error because it can not read the file persistence.xml in the web start. thx
Posted by: sso on October 10, 2006 at 04:02 AM
-
Hi sso,
I see you have also posted a forum thread http://forums.java.net/jive/thread.jspa?threadID=19006&tstart=0 on using Java Web Start and creating EMF. So, let's discuss the issue in that forum.
Sahoo
Posted by: ss141213 on October 10, 2006 at 10:40 PM
-
I am concern about the dynamic query in JPA. I am using IBATIS now. It is a great framework, specially for dynamic query. All search criteriaes and operators in where clause can be dynamic based on the value of searching parameters. So from coding pointview, it is very easy to handle.
I am wondering whether JPA has similar functionality.
Thanks,
Joe
Posted by: smell on January 29, 2007 at 11:45 AM
-
JPA supports two types of query languages: SQL and JPA QL (successor of EJB QL). None of them supports the kinds of feature you have requested.
Posted by: ss141213 on January 29, 2007 at 07:29 PM
|