Thursday Jul 31, 2008
Thursday Jul 31, 2008
With the addition of support for scripting languages in the Java platform, there has been a lot of interest in combining into web applications scripting languages such as Groovy, Java technologies such as the Java Persistence API (JPA), and databases such as MySQL. Last year I wrote a Tech Tip titled Combining JavaServer Faces Technology, Spring, and the Java Persistence API that showed how you can use JavaServer Faces Technology, Spring, and the JPA to create an application that displays an online catalog of pets. In this tip, I'll show you how to create an online catalog application using the Groovy language, the Grails framework, the MySQL database, and the Java Persistence API.
A package that contains the code for the sample application accompanies the tip. The code examples in the tip are taken from the source code of the sample (which is included in the package). In this tip, you'll use NetBeans IDE 6.5 Milestone 1 to build the application and deploy it on the GlassFish application server. The NetBeans IDE is a modular, standards-based, integrated development environment (IDE) written in the Java programming language. The latest NetBeans IDE offering, NetBeans IDE 6.5 Milestone 1 (or M1 for short), offers many new features including support for Groovy and Grails. GlassFish is a free, open source application server that implements the newest features in the Java EE 5 platform.
A Summary of the Languages, Technologies, and Frameworks in the Sample Application
If you're not familiar with Groovy, Grails, MySQL, or the Java Persistence API, here are brief descriptions:
The Sample Application
The sample application displays an online catalog of pets sold in a pet store. Figure 1 shows the Catalog Listing page, which allows a user to page through a list of items in a store.
|
|
Figure 1. Catalog Listing Page |
Examining the Application
Earlier I mentioned that Grails is a Model-View-Controller based framework that simplifies the development of web applications. The online catalog application uses Grails and so it follows the MVC pattern, that is, the application isolates its data, the "Model", from the user interface, the "View", and from the code that manages the communication between the model and the view, the "Controller". Let's first look at the Model for the application.
The Model
The Model not only represents the data for the application, but it also represent persistent data, that is, data that persists beyond the life of the application. In other words, the Model represents an application's persistent business domain objects. The application uses JPA to manage that persistence. In JPA, an entity instance -- an instance of an entity object -- represents a row of data in a database table.
If you examine the source code for the application, you'll find the following two classes in the model directory:
Item and Address. Item is an entity class -- a typical JPA entity object -- that
maps to an item table in a database. The table stores information about items in the catalog. Here is part
of the source code for the Item class:
package model;
import java.io.Serializable;
...
@Entity
@Table(name = "item")
public class Item implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private Long id;
private String name;
private String description;
private String imageurl;
private String imagethumburl;
private BigDecimal price;
@ManyToOne(optional = false)
@JoinColumn(name = "address_id")
private Address address;
// getters and setters
...
}
Address is an entity class that maps to an address table in the database. The table stores
addresses associated with items in the catalog. Here is part of the source code for the Address class:
package model;
import java.io.Serializable;
...
@Entity
public class Address implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private Long id;
private String street1;
private String street2;
private String city;
private String state;
private String zip;
private BigDecimal latitude;
private BigDecimal longitude;
private BigInteger version;
@OneToMany(fetch = FetchType.EAGER,
cascade = { CascadeType.ALL },
mappedBy = "address")
private Collection<Item>items = new ArrayList();
// getters and setters
...
}
The Item class has a many-to-one relationship with the Address class, meaning that
there can be multiple items in the catalog associated with the same address, but multiple addresses cannot be associated
with the same item. This relationship is specified by the @ManyToOne annotation in the Item class
and the @OneToMany(mappedBy = "address") annotation in the Address entity class.
Using JPA Entities With Grails and MySQL
To use the JPA entities for the application with Grails and MySQL, you first need to create a Grails application and then modify some files in the Grails application directory structure.
We'll use NetBeans IDE 6.5 M1 to create a Grails application. If you haven't already done so, download NetBeans IDE 6.5 Milestone 1 and download Grails.
Start NetBeans IDE 6.5 Milestone 1. Select New Project from the File menu. Then select Groovy in the Categories window and Grails in the Projects window as shown in Figure2.
|
|
Figure 2. Creating a Grails Project in NetBeans IDE 6.5 M1 |
Click the Next button and name the project, for instance, MyGrailsApp. Accept the default project location
or browse to select a different location. Leave the Set as Main Project checkbox checked and click the Finish button.
In response, NetBeans creates the Grails project and a standard directory structure for a Grails application. Figure 3 shows the Grails directory structure for the online catalog application.
|
|
|
Figure 3. Grails Directory Structure for the Online Catalog Application |
After you have your directory structure in place, do the following:
Item and Address entity files for the online catalog application are in the
catalog\src\java\model directory.app_name\lib directory.
You can find the mysql-connector-java-5.1.6-bin.jar file for the online catalog application in the
catalog\lib directory.DataSource.groovy file in the app_name\grails-app\conf directory to use MySQL
as the database and specify the GrailsAnnotationConfiguration configuration class to use the annotations
in the JPA entities. The code marked in bold in the following code example shows the additions and modification that I made
to the DataSource.groovy file for the online catalog application.
hibernate.cfg.xml file to the app_name\grails-app\conf\hibernate directory.
Here is the hibernate.cfg.xml file for the online catalog application. You can find it in the
catalog\grails-app\conf\hibernate directory.
The Controller
NetBeans IDE 6.5 M1 enables you to create domain classes and controllers, but I haven't found the menu option to generate controllers, so for now, let's use the command line as follows to generate a controller:
grails generate-controller domain-classwhere domain-class is the domain class name. For example, to generate a controller for the
Item
domain class in the online catalog application, I entered the following command:
grails generate-controller model.Item
In response, the command generates a file named domain-classController.groovy in the
grails-app/controllers directory. For the Item class, the generated controller is in
grails-app/controllers/ItemController.groovy.
Figure 4 shows the controller, ItemController.groovy, in the NetBeans IDE 6.1 M1 Groovy
editor window.
|
|
Figure 4. A Controller Listed in the Grails Directory Structure for the Online Catalog Application |
Controllers handle incoming HTTP requests, interact with the model to get data and process requests, invoke the correct
view, and direct domain data to the view for display. In Grails, HTTP requests are handled by controller classes that contain
one or more action methods that are executed on request. The action methods either render
a Groovy Server Page
(GSP) or redirect to another action. Grails routes requests to the controller action that corresponds to the URL mapping for
the request. In Grails, the default mapping from URL to action method follows the convention
http://host/app_name/controller/action/id where host is the host name,
app_name is the name of the Grails
application, controller is the controller class, action is the action method, and id
is the id of a passed parameter. For example, the URL
http://host/catalog/item/list calls the list action method in the item controller class. Here is code snippet in
ItemController.groovy that shows the list method:
Grails scaffolding provides a series of standardized controller
action methods for listing, showing, creating, updating, and deleting objects of a class. These standardized actions come
with both controller logic and default view Groovy Server Pages. The list action in ItemController
renders a view with a paginated list of item objects.
If a URL has a controller but no action, as is the case for http://localhost:8080/catalog/item/, Grails defaults to the
index action. In the ItemController code, the index action method redirects to the
list action method. The list action method calls the Item.list() method, which
returns an ArrayList of item objects retrieved from the item table in the
database. If there are more objects in the table than the number specified in params.max (in this case, 10),
Grails automatically creates next and previous pagination links. The Grails framework automatically makes the
itemList variable available to the view.
After executing code, actions usually render a GSP in the views directory corresponding to the name of the
controller and action. For example the list action renders the GSP, list.gsp, in the
grails-app\views\item directory.
The View
The view layer uses data from domain objects provided by the controller to generate a web page. In Grails, the view is rendered using Groovy Server Pages. To generate the view, open a command prompt, navigate to the project directory for your Grails application, and enter the following command:
grails generate-views domain-class
where domain-class is the domain class. For example, to generate a view for the Item
domain class in the online catalog application, I entered the following command:
grails generate-views model.Item
In response, the command generates GSPs for the domain class. For example, it generates create.gsp,
edit.gsp, list.gsp, model.Item entity.
Here is part of the list.gsp file for the online catalog application. Note that I modified the HTML
table format that is generated by default to display the pet images.
The view uses instance variables set by the controller to access the data it needs to render the GSP. Groovy Server Pages
use a GroovyTagLib that is similar to the JSP tag library. Notice the tags that start with <g:
in the list.gsp code. These are GroovyTags. Here is a brief summary of the GroovyTags and some other
elements in the list.gsp code:
<g:sortableColumn>
<g:each in="${itemList}" status="i" var="item">
itemList variable, which is an ordered ArrayList
of Item model objects, and assigns each Item model object to the item variable.
<g:link action="show" id="${item.id}">${item.name?.encodeAsHTML()}</g:link>
href based on the specified action, id, and controller parameters specified.
In this example, it generates a link to the item/show/id action. This action will display the
corresponding item details. Here, the line generates the following HTML for the variable item:
<a href="/catalog/item/show/2">Friendly Cat</a>
<img src="${createLinkTo(dir:'images',file:item.imagethumburl)}"/>
${item.price?.encodeAsHTML()}
<g:paginate total="${Item.count()}" />
The Show Action Method
Recall that in Grails, the default mapping from URL to action method follows the convention
http://host/app_name/controller/action/id where host is the host name,
app_name is the name of the Grails
application, controller is the controller class, action is the action method, and id
is the id of the passed parameter. This means that in the online catalog application, a URL of http://host/item/show/1
will route to the show action in the ItemController, passing 1 to the method as the id of the parameter.
The show action of the ItemController class is shown below. The ItemController
show action renders a view showing the details of the item object corresponding to the
id parameter.
The show action method calls the Item.get() method. That method, in turn, queries
the items table, and returns an item instance variable that corresponds to the
item whose attribute id (that is, its primary key) is equal to the id parameter.
This is the equivalent to the following SQL statement:
select * from items where id='1'
The Grails framework automatically makes the item variable available to the Show view.
The Show Item GSP
After executing the appropriate code, the show action renders the show.gsp file
in the applications's views/item directory. Here is the part of the show.gsp file
for the online catalog application, presenting the item show view:
Here are some important parts of the item show view:
${item.description}
<img src="${createLinkTo(dir:'images',file:item.imageurl)}" /> ${item.description}
imageurl attribute.
${item?.address?.city}
Running the Sample Code
These instructions assume that you have NetBeans IDE 6.1, GlassFish v2ur2, and MySQL installed. You can download all three in a single bundle. Another option is to download a bundle that includes Sun Java System Application Server 9.1 Update 1 with MySQL Community Server.
GF_install\updatecenter\bin\updatetool
|
<sample_install_dir>/catalog, where <sample_install_dir> is the directory
where you unzipped 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:\catalog.
|
|
|
catalog.sql file in the catalog directory
and paste the contents into the SQL command window.catalog\grails-app\conf\DataSource.groovy file are the same as the corresponding property settings in NetBeans
IDE 6.5 M1 for the MySQL server database.catalog-0.1.war file in your the catalog project
directory. You might need to change the level of Grails expected by the application -- this is the
app.grails.version setting in the catalog\application.properties file -- to align it
with the level of Grails you have installed.
catalog-0.1.war file to the GF_install/domains/domain1/autodeploy
directory, where GF_install is the directory where you installed GlassFish.http://localhost:8080/catalog-0.1/ in your browser. You should see the home page of the
sample application.
|
Further Reading
About the Author
Carol McDonald is a Java Technology Evangelist at Sun Microsystems. As a software developer since 1986, Carol's experience has been in the technology areas of distributed network applications and protocols, including Java EE technology, XML, Internet/Intranet applications, LDAP, Distributed Network Management (CMIP,SNMP) and Email (X.400,X.500). Besides Java, Carol is also fluent in French and German.
1 As used on this web site, the terms "Java virtual machine" or "JVM" mean a virtual machine for the Java platform.
|
|
Project SailFin and Ericsson Application Competition
Create or enhance an IMS client-server application for a chance to win USD $5,000 or a Sony Ericsson phone.
Community Code Samples
Use code samples for your own programming and add your own examples to this collection.
This is pretty much exactly what I've been looking for.
My team invested a lot of effort in learning our previous stack of Hibernate JPA and Apache Wicket but we now develop almost all of our web apps in Grails. Of course GORM is great but it was nice to see the clean separation of concerns in your example. JPA is a standard that we (and others) know well so I'm looking forward to trying this on a future project.
On an unrelated note, I hope Sun does a better job of pushing Grails and continues to encourage improvements to Groovy tooling. For us, the transparent linking and bicompilation between Groovy and Java is a huge win.
Posted by Bill Shim on August 01, 2008 at 02:25 PM PDT #
I have a question: what advantages would I have to use JPA for my domain model over using plain GORM?
Besides yours (which is very nice), I saw other presentations on how to do this, but I can't tell if this is just a convenient way to connect to a legacy (well, let's say existing) domain model, Or if there are real advantages to do so.
Thank you.
Posted by JS on August 04, 2008 at 10:18 AM PDT #
Thanks Bill, I agree that Groovy and Java is a huge win. We have been and will continue talking about and demoing Grails and Glassfish at some of our Tech Days events http://developers.sun.com/events/techdays/
Posted by carol mcdonald on August 04, 2008 at 01:19 PM PDT #
If you have existing data base tables then JPA is a nice option for a Java O/R framework. JPA is a standard api for O/R mapping, it draws upon the experience of Hibernate, TopLink, and JDO.
Posted by carol mcdonald on August 04, 2008 at 01:46 PM PDT #
Thanks Carol for this great article.
I was wondering whether you have an idea of how to make the @Version field work with Grails.
I tried adding a version field and performed simultaneous updates using different browsers but didn't get any errors.
Also, would you know how to add constraints (for input validation) to JPA entities similar to how you do it in Grails domain objects?
Super thanks!
Posted by Paul on August 12, 2008 at 07:06 PM PDT #
Thanks Carol, you're great.
I can't seem to find any article or references to using EJB3.0 in Grail or Groovy applications. My team and I are handling a full enterprise application running EJBs in the middle tier and I was thinking of trying Grail out before proposing it.
All i seem to find is database-backed web application.
Posted by Michael Enudi on August 15, 2008 at 02:49 AM PDT #
Recommendation for German readers:
Visit http://www.groovy-forum.de or http://www.groovy-forum.de/wiki/ for more information and discussion about Groovy and Grails.
Posted by Christian on August 15, 2008 at 03:37 AM PDT #
A great article by Jason Rudolph:
http://www.infoq.com/articles/grails-ejb-tutorial
Posted by JS on August 15, 2008 at 04:08 AM PDT #
very good
Posted by diana on August 15, 2008 at 07:14 AM PDT #
JPA 1.0 persistence providers must support optimistic locking where a numeric or timestamp column tracks the version. Since the persistence provider takes care of this it should work with Grails too. This article has an example of using this: http://www.oracle.com/technology/pub/articles/gupta-jpa.html
With JPA 1.0 you can specify uniqueness and nullable constraints on columns. With JPA 2.0 you can use bean validation on entities (JSR 303). The url I mentioned above has an example of doing validation using @prepersist.
Posted by carol mcdonald on August 15, 2008 at 07:55 AM PDT #
yes for more info on grails and ejb3 see http://www.infoq.com/articles/grails-ejb-tutorial
Posted by carol mcdonald on August 15, 2008 at 08:59 AM PDT #
Just to be clear EJB3 and JPA are separate specs, and JPA entities are not EJBs
Posted by carol mcdonald on August 15, 2008 at 09:02 AM PDT #
TOO GOOD !!!!! well done , java is the best ...
Posted by indy on August 16, 2008 at 08:44 AM PDT #
muy bueno
Posted by cristina carrete on August 16, 2008 at 02:37 PM PDT #
TOO GOOD !!!!! well done , java is the best ...
Posted by cristina carrete on August 16, 2008 at 02:39 PM PDT #
I wonder if Amazon SimpleDB together with SimpleJPA (http://code.google.com/p/simplejpa/) would be feasible?
Posted by Roshan Shrestha on August 21, 2008 at 11:55 AM PDT #
In response to the question of Gorm over JPA. Gorm ties you to grails. If you wanted to write a Swing admin interface in addition to a client web interface you would have to learn a lot of advanced tricks to use Gorm outside of Grails with Swing. If you use JPA then you don't have that problem. Most people will read this and think "I don't need Swing" however it is interesting to think about using JUnit and other "out of container tools" such as if you need to write a batch update java programs that might need to act on your data at regular intevals using 'normal java' outside of Grails. If you are just writing a pure website on Grails then just use Gorm. If you are writing a database that is more than a website then consider JPA mixed with Grails so that you can use Grails for its website power.
Posted by simbo on August 21, 2008 at 12:48 PM PDT #
when i run the above project it show the below error. How can i resolve it. if any body please reply me......
Application expects grails version [1.0.2], but GRAILS_HOME is
version [1.1.1] - use the correct Grails version or run
'grails upgrade' if this Grails version is newer than the version your application expects.
Posted by Shantanu on June 21, 2009 at 10:44 PM PDT #