Wednesday, February 29, 2012

XML - JAXB Parser

With DOM and SAX parsers, it is necessary to know the structure of the XML to extract the data in it.

Oftentimes, the exact structure of the XML is not important. What is more important to us is the data in it.

For example, in our users.xml, what we really need is the user's first and last name.
The exact structure of the XML is not important for us.

JAXB - Java API for XML Binding - allows us to directly get the data in the XML. We do not need to worry about the XML structure.

JAXB uses the XSD - XML Schema Descriptor - to generate classes which it can populate with the XML's data.

An XSD describes the structure of the XML. It is part of the formal specification of the XML. You expect to get it with the XML.

Let us use JAXB to parse users.xml.

First we need to have the XSD. In your /data folder, create a files users.xsd, like this:
 <?xml version="1.0"?>  
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="users">
<xs:complexType>
<xs:sequence>
<xs:element name="user" type="userType" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="userType">
<xs:sequence>
<xs:element name="firstName" type="xs:string" minOccurs="1" maxOccurs="1"/>
<xs:element name="lastName" type="xs:string" minOccurs="1" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
</xs:schema>

This XSD says that the XML will have a root element 'users', which can have any number of elements 'user', which will have two elements - firstName and lastName, each of type String.

Our users.xml confirms to this XSD.

Now, let us ask JAXB to generate the classes for this XSD.

JAXB is already shipped as part of your JDK. So we can run it from a command prompt.

Open a command prompt, cd to your /HelloWorld/data folder, and give it the following command:
 C:\Documents and Settings\LavanniM\workspace-confucius\HelloWorld\data>xjc -p org.confucius.jaxb users.xsd -d "c:\Documents and Settings\LavanniM\workspace-confucius\HelloWorld\src"  
parsing a schema...
compiling a schema...
org\confucius\jaxb\ObjectFactory.java
org\confucius\jaxb\UserType.java
org\confucius\jaxb\Users.java

xjc is the JAXB command line executable, -p tells JAXB which package the classes it will generate should belong to, -d tells it which directory to put the classes in.

Once we have generated these classes, we are ready to parse using JAXB.

In your /src/org/confucius folder, create a class TestJAXBParser.java, like this:
 package org.confucius;  

import java.io.File;
import java.util.Iterator;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;

import org.confucius.jaxb.UserType;
import org.confucius.jaxb.Users;

public class TestJAXBParser {
public static void main (String[] args){
try {
JAXBContext jc = JAXBContext.newInstance("org.confucius.jaxb");
Unmarshaller unmarshaller = jc.createUnmarshaller();
Users users = (Users) unmarshaller.unmarshal(new File("data/users.xml"));

for (Iterator<UserType> iter = users.getUser().iterator(); iter.hasNext();){
UserType user = iter.next();
System.out.println("Got user: " + user.getFirstName() + " " + user.getLastName());
}
}
catch (Exception e){
e.printStackTrace();
}
}
}

We create a JAXBContext, pointing it to the package where we have the JAXB classes, the create an 'unmarshaller' to parse the XML file.

A marshaller is an object which can convert an object to XML, an unmarshaller converts XML to objects.

R-click on the TestJAXBParser.java file in your Eclipse Navigator View, select Run As->Java Application.

You will see the users printed to the console.

Note that we extracted data from the XML - but our extract code has no knowledge of the XML (the XSD is not a part of the extract code, but a part of the XML formal specification). JAXB did whatever is necessary to give us the final objects.

Tuesday, February 28, 2012

XML - SAX Parser

DOM parsers work well if the XML file is small, and can be loaded into memory all at once.

But oftentimes, the XML files contain millions of records - for example if it is a dump from a database. In this case, attempting to load the file into memory all at once will cause memory issues.

SAX - Java Simple API for XML - parses XML files element by element, so it only has one element in memory at any time. It calls user specified 'handler' each time it reads an element.

This style of parsing is often called "event" driven. Every time the SAX parser encounters an element, it is an event.

Let us see how to parse the users.xml file, this time with a SAX parser.

For this, we need to write a "handler".

In your /src/org/confucius folder, create a class UserSAXHandler.java, like this:
 package org.confucius;  

import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class UserSAXHandler extends DefaultHandler {
private boolean firstNameFlag = false;
private boolean lastNameFlag = false;
private String firstName = null;
private String lastName = null;

public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
if (qName.equals("firstName")){
firstNameFlag = true;
}
else if (qName.equals("lastName")){
lastNameFlag = true;
}
}

public void characters(char[] ch, int start, int length)
throws SAXException {
if (firstNameFlag){
firstName = new String(ch, start, length);
}
else if (lastNameFlag){
lastName = new String(ch, start, length);
}
}

public void endElement(String uri, String localName, String qName) throws SAXException {
if (qName.equals("firstName")){
firstNameFlag = false;
}
else if (qName.equals("lastName")){
lastNameFlag = false;
System.out.println("got User: " + firstName + " " + lastName);
}
}
}

We are handling two SAX events - the start of a element, and the end of an element.
In between these, we extract the value specified for the element.

There are other "events" that we could have handled if we wished to - such as the start of the document, the end of the document, etc. The interface ContentHandler lists all the events that a SAX parser can expect to handle.

Let us now write a test for this parser.

In your /src/org/confucius, create a class TestSAXParser.java, like this:
 package org.confucius;  

import java.io.File;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.helpers.DefaultHandler;

public class TestSAXParser {
public static void main (String[] args){
try {
DefaultHandler handler = new UserSAXHandler();
SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
saxParser.parse(new File("data/users.xml"), handler);
}
catch (Exception e){
e.printStackTrace();
}

}
}

We create a SAXParser, then pass it the XML file and the handler.

R-click on the TestSAXParser.java file in your Eclipse navigator.
Select Run As --> Java Application.

You will see the users printed to the console.

Monday, February 27, 2012

XML - DOM Parser

Java XML DOM parser is the easiest API for parsing XML files.

With this parser, we load the entire XML file into memory. Then extract any element with its name, extract its child elements, etc.

Let us see this with an example.

We will parse a XML file which contains a couple of Users - their first and last names.

In your /data folder, create a file users.xml, like this:
 <users>  
<user>
<firstName>Jenny</firstName>
<lastName>Lee</lastName>
</user>
<user>
<firstName>Benny</firstName>
<lastName>Carlson</lastName>
</user>
</users>

Now let us use the Java XML DOM parser to extract both the users from this file.

In your /src/org/confucius folder, create a class UserDOMParser.java, like this:
 package org.confucius;  

import java.io.File;

import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;


public class UserDOMParser {

public static void main(String[] args) {
try {
File usersFile = new File("data/users.xml");
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(usersFile);

NodeList users = doc.getElementsByTagName("user");

for (int i = 0; i < users.getLength(); i++) {
Element user = (Element) users.item(i);

String firstName = user.getElementsByTagName("firstName").item(0).getTextContent();
String lastName = user.getElementsByTagName("lastName").item(0).getTextContent();

System.out.println("Got User: " + firstName + " " + lastName);
}

} catch (Exception e) {
e.printStackTrace();
}
}
}

What are we doing here?
We used the DocumentBuilder to read the XML file, then extracted the <user> elements, looped over and printed out the first and last names of each user.

R-click on UserDOMParser.java in your Eclipse Navigator view and select Run As->Java Application.

You will see the users printed to the console.

Sunday, February 26, 2012

JNDI example

We will use JNDI to access a Tomcat Datasource.

What is a Tomcat Datasource?

One of the facilities Tomcat provides is to maintain JDBC datasources.

Instead of each application maintaining a datasource and connection pools to the datasource, it is possible to pass that responsibility to Tomcat.

Once Tomcat is configured with a Datasource, applications can simply request the datasource from Tomcat whenever they need it. The configuration details about the datasource are not known by the application. All the applications know is the JNDI name of the datasource.

Let us configure Tomcat to create a datasource to the confuciusDB.

Open the file context.xml in your <tomcat_install_dir>/config and update it, like this:
 <?xml version='1.0' encoding='utf-8'?>  
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- The contents of this file will be loaded for each web application -->
<Context>

<!-- Default set of monitored resources -->
<WatchedResource>WEB-INF/web.xml</WatchedResource>

<!-- Uncomment this to disable session persistence across Tomcat restarts -->
<!--
<Manager pathname="" />
-->

<!-- Uncomment this to enable Comet connection tacking (provides events
on session expiration as well as webapp lifecycle) -->
<!--
<Valve className="org.apache.catalina.valves.CometConnectionManagerValve" />
-->
<Resource name="jdbc/confuciusDB" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="10000"
username="confucius" password="changeit" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/confuciusDB"/>

</Context>
We have created a datasource with the name "jdbc/confuciusDB" providing it with the database URL, username, password and specifications for the connection pool.

Now we can use JNDI to get this datasource from inside our code.
Update your HelloWorld.java, like this:
 package org.confucius;  

import java.io.IOException;
import java.sql.Connection;
import java.sql.Statement;

import javax.naming.InitialContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;

public class HelloWorld extends HttpServlet{
public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
try {
// Use JNDI to get the datasource from tomcat
InitialContext cxt = new InitialContext();
DataSource ds = (DataSource) cxt.lookup( "java:/comp/env/jdbc/confuciusDB" );
response.getWriter().println("DataSource = " + ds);

// Now we can use the datasource (this code block has nothing to do with JNDI)
Connection conn = ds.getConnection();
Statement stmt = conn.createStatement();
stmt.execute("create table fruits (name VARCHAR(32), quantity INT)");
stmt.close();
conn.close();
}
catch (Exception e){
e.printStackTrace(response.getWriter());
}
}
}


We first open a JNDI context, then use the lookup() method.
Note that we have used the full tomcat JNDI name of the datasource.

The exact name of the resource is decided by the service provider, in this case, tomcat.

Once we have the datasource, we can open connections and run SQL queries.

JNDI has helped us get the datasource from Tomcat. Tomorrow, if Tomcat changes its datasource management implementation, our code will still work without any changes (provided Tomcat does not change the datasource name).


Update your web.xml to map the HelloWorld servlet, like this:
 <web-app>  
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>org.confucius.HelloWorld</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>

</web-app>



Build and deploy HelloWorld.war, then point your browser to:
http://localhost:8080/HelloWorld/hello

You will see the datasource printed to the browser.

If you open your mysql command line client, you can see the tabel 'fruits' created:
 mysql> show tables;  
+-----------------------+
| Tables_in_confuciusdb |
+-----------------------+
| cart |
| fruits |
| user |
+-----------------------+
3 rows in set (0.00 sec)

JNDI & Directory Services

JNDI is a Java standard API for accessing Directory Services.

So, what are directory services?

Directory services are essentially "lookups".

Suppose you wanted John's phone number, you can lookup the phone directory.
If you wanted to order a pizza, you lookup the yellow pages.

These are traditional examples of directory services.

Let us look at some more abstract directory services.

Suppose your server administrator kept a secured file which contained a list of users allowed to use your application.

Your application would lookup this file whenever someone logged in and tried to use your application.

This file is a directory service.

Suppose your administrator maintained a database server, where he kept all the users and their privileges.

Now you would lookup this database server to verify user privileges. This server is now providing a directory service.

What make a directory service a useful programming model?

A directory service usually contains information that usually does not change often. Like people's phone numbers, pizza shops, user privileges.

They are usually kept up-to-date by an administrator or authority.

Users of this information do not usually have any role to play in its creation or maintenance.

Why do we need JNDI?
Because directory services are not maintained by the user, the user has no control over their implementation. Those who provide the directory service, called 'Service Providers', can change implementation as necessary. They might move their implementation from file based to database to improve security. They might change their database to improve efficiency.

None of this should affect the user.

JNDI provides a standard way to lookup the directory service. It separates the implementation of the service from the usage.

It does so by the use of a JNDI "Context".

Every service provider is expected to provide a library which provides access to their directory service. They can create one of their own, or use any one of the standard libraries.

Users are then able to load a "Context" object for this directory service.

Once they have this object, they can lookup anything they want from that directory service.

JNDI is a named directory service interface.

Users can do a lookup by providing a string (name), just like they would use a key to a hashtable.

The directory service is expected to return the object corresponding to this name.

Saturday, February 25, 2012

web.xml - context & init params

You can specify parameters to your web application in the web.xml, using the tags <context-param> and <init-param>.

The former are "global" - they can be accessed by all servlets.
The later are servlet-specific.

Let us see this with an example.

We will declare a 'color' and a 'size'.

Update your web.xml, like this:
 <web-app>   
<context-param>
<param-name>color</param-name>
<param-value>red</param-value>
</context-param>

<servlet>
<servlet-name>hello1</servlet-name>
<servlet-class>org.confucius.HelloWorld</servlet-class>
<init-param>
<param-name>size</param-name>
<param-value>medium</param-value>
</init-param>
</servlet>

<servlet>
<servlet-name>hello2</servlet-name>
<servlet-class>org.confucius.HelloWorld</servlet-class>
<init-param>
<param-name>size</param-name>
<param-value>large</param-value>
</init-param>
</servlet>

<servlet-mapping>
<servlet-name>hello1</servlet-name>
<url-pattern>/hello1</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>hello2</servlet-name>
<url-pattern>/hello2</url-pattern>
</servlet-mapping>

</web-app>

We have declared two servlets, hello1 and hello2 - both of which point to the same class HelloWorld.
One is configured with a size 'medium, the other with size 'large'.

Let us now write HelloWorld.java (in your /src/org/confucius) which will read these parameters, like this:
 package org.confucius;  

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class HelloWorld extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String color = getServletContext().getInitParameter("color");
String size = getServletConfig().getInitParameter("size");
response.getWriter().println("Color is " + color);
response.getWriter().println("Size is " + size);
}
}

<context-param> is accessed via ServletContext and <init-param> via ServletConfig.

Build HelloWorld.war and deploy it, then point your browser to:
http://localhost:8080/HelloWorld/hello1

You will see the color set to red and the size to medium.

Now point your browser to:
http://localhost:8080/HelloWorld/hello2

You will see the color set to red and the size to large.

Thus, each servlet accesses its own init-param, but they all access the same context-param.

Listeners

Sometimes your web application needs to know about lifecycle events - such as when a new session starts, a session expires, an attribute is added to the context, etc.

You can write a Listener class and register it in web.xml.

In your /src/org/confucius folder, create a class SessionTimeoutListener.java, like this:
 package org.confucius;  

import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

public class SessionTimeoutListener implements HttpSessionListener {
private Logger logger;

public SessionTimeoutListener(){
PropertyConfigurator.configure("log4j.properties");
logger = Logger.getLogger(HelloWorld.class);
}

public void sessionCreated(HttpSessionEvent event) {
logger.info("Session created.");
}

public void sessionDestroyed(HttpSessionEvent event) {
logger.info("Session destroyed.");
}

}

This is a Listener class - it implements the HttpSessionListener interface.
It is notified whenever a session is created and destroyed.

Note that we have used log4j to write logs - this post explains how to use log4j.

Register this class in your web.xml, like this:
 <web-app>   
<listener>
<listener-class>org.confucius.SessionTimeoutListener</listener-class>
</listener>

<session-config>
<session-timeout>1</session-timeout>
</session-config>
</web-app>


That's it.
Note that we are specifying a session timeout of 1 minute.

In your /web-content/jsp folder, create a file home.jsp, like this:
 <html>  
<body>
<p>Welcome to Confucius</p>
</body>
</html>

Build and deploy HelloWorld.war, then point your nrowser to:
http://localhost:8080/HelloWorld/jsp/home.jsp

This will start a new session.

If you open your HelloWorld.log file (in <tomcat_install_dir>/logs), you will see a log for when the session was created.

web.xml -- welcome-file-list

<welcome-file-list> is a way to specify the default page a user will see.

For example, update your web.xml like this:
 <web-app>   
<welcome-file-list>
<welcome-file>welcome.html</welcome-file>
<welcome-file>index.html</welcome-file>
<welcome-file>home.html</welcome-file>
</welcome-file-list>
</web-app>

This means that web server will look for welcome.html / index.html / home.html in that order and display the first one it finds.

In your /web-content folder, create a file home.html, like this:
 <html>  
<body>
<p>Welcome to Confucius</p>
</body>
</html>

Build and deploy HelloWorld.war, the point your browser to:
http://localhost:8080/HelloWorld/

You will see the home.html page displayed.

Spring MVC - Wiring

First we update web.xml to direct all requests to the Spring MVC Servlet, like this:
 <web-app>   
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>


Now, in your /WEB-INF folder, create a file springmvc-servlet.xml to declare how to wire the MVC, like this:
Note that the name of the file is based on the Servlet name in web.xml (springmvc).
 <?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<bean name="/register.do" class="org.confucius.HelloWorldController"/>

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>

</beans>

Here we are directing all the /register.do requests to the HelloWorldCOntroller.
All Views returned by the controllers will be directed to the /jsp/.jsp file.

In your /src/org/confucius folder, create a class HelloWorldController.java, like this:
 package org.confucius;  

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

public class HelloWorldController implements Controller {

public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
String firstName = request.getParameter("firstName");
String lastName = request.getParameter("lastName");

String aMessage = "Welcome to Spring MVC, " + firstName + " " + lastName + "!";

ModelAndView modelAndView = new ModelAndView("hello_world");
modelAndView.addObject("message", aMessage);

return modelAndView;
}

}

We get the first and last names from the request, and create a message from it.
We create a view "hello_world" and pass it the message.

Note that Spring MVC will redirect to the view /jsp/hello_world.jsp - based on our prefix and suffix configuration in springmvc-servlet.xml.

In your /jsp folder, create a file hello_world.jsp, like this:
 <html>  
<body>
<p>${message}</p>
</body>
</html>

Build and deploy HelloWorld.war, then point your browser to:
http://localhost:8080/HelloWorld/jsp/registerForm.jsp
to see Spring MVC in action.

Spring MVC - Setup

In a previous post we saw how to use Struts.

Spring also has a MVC module, which works similar to Struts.

Let us do the same example as the Struts post, but this time with Spring MVC.

We will have a Registration form, which will collect the users first and last name.
Then we will show a confirmation message.

We will use Spring MVC to wire the model, view and controller.

First, update your ivy.xml to get the spring-web.jar & spring-webmvc.jar.
My ivy.xml now looks like this:
 <ivy-module version="2.0">  
<info organisation="org.confucius" module="helloworld"/>
<dependencies>
<dependency org="org.springframework" name="spring-webmvc" rev="2.5.6"/>
<dependency org="org.springframework" name="spring-web" rev="2.5.6"/>
<dependency org="org.springframework" name="spring" rev="2.5.6"/>
<dependency org="commons-pool" name="commons-pool" rev="20030825.183949"/>
<dependency org="commons-dbcp" name="commons-dbcp" rev="20030825.184428"/>
<dependency org="antlr" name="antlr" rev="20030911"/>
<dependency org="javassist" name="javassist" rev="3.12.1.GA"/>
<dependency org="mysql" name="mysql-connector-java" rev="5.1.18"/>
<dependency org="javax.persistence" name="persistence-api" rev="1.0.2"/>
<dependency org="jboss" name="jboss-j2ee" rev="4.2.2.GA"/>
<dependency org="org.jboss.logging" name="jboss-logging" rev="3.1.0.GA"/>
<dependency org="org.slf4j" name="slf4j-simple" rev="1.6.1"/>
<dependency org="org.slf4j" name="slf4j-api" rev="1.6.4"/>
<dependency org="org.hibernate" name="hibernate3" rev="3.6.0.Final"/>
<dependency org="dom4j" name="dom4j" rev="1.6.1"/>
<dependency org="struts" name="struts" rev="1.2.9"/>
<dependency org="org.apache.struts" name="struts-taglib" rev="1.3.10"/>
<dependency org="commons-collections" name="commons-collections" rev="20040616"/>
<dependency org="commons-digester" name="commons-digester" rev="2.1"/>
<dependency org="commons-beanutils" name="commons-beanutils" rev="20030211.134440"/>
<dependency org="commons-logging" name="commons-logging" rev="1.1.1"/>
<dependency org="org.apache.httpcomponents" name="httpcore" rev="4.2-alpha2"/>
<dependency org="org.apache.httpcomponents" name="httpclient" rev="4.2-alpha1"/>
<dependency org="org.apache.commons" name="commons-exec" rev="1.1"/>
<dependency org="com.google.guava" name="guava" rev="r09"/>
<dependency org="org.seleniumhq.selenium" name="selenium-api" rev="2.17.0"/>
<dependency org="org.seleniumhq.selenium" name="selenium-remote-driver" rev="2.17.0"/>
<dependency org="org.seleniumhq.selenium" name="selenium-firefox-driver" rev="2.17.0"/>
<dependency org="org.seleniumhq.selenium" name="selenium-java" rev="2.16.1"/>
<dependency org="junit" name="junit" rev="4.10"/>
<dependency org="org.json" name="json" rev="20090211"/>
<dependency org="javax.servlet" name="servlet-api" rev="2.5"/>
<dependency org="javax.servlet" name="jsp-api" rev="2.0"/>
<dependency org="jstl" name="jstl" rev="1.2"/>
<dependency org="log4j" name="log4j" rev="1.2.16"/>
</dependencies>
</ivy-module>

Run the Ant 'resolve' task to download the spring-web.jar and spring-webmvc.jar to your /lib folder

In Eclipse, go to Project-->Properties-->Java Build Path-->Libraries-->Add Jars
and add the spring-web.jar & spring-webmvc.jar

You need to also update the Classpath in your build.xml, which now looks like this:
 <project name="HelloWorld" xmlns:ivy="antlib:org.apache.ivy.ant" >  

<target name="resolve" description="--> retrieve dependencies with ivy">
<ivy:retrieve />
</target>

<target name="init" depends="resolve">
<mkdir dir="classes"/>
<mkdir dir="target"/>
</target>

<path id="build.classpath">
<pathelement location="lib/hibernate3-3.6.0.Final.jar"/>
<pathelement location="lib/spring-webmvc-2.5.6.jar"/>
<pathelement location="lib/spring-web-2.5.6.jar"/>
<pathelement location="lib/spring-2.5.6.jar"/>
<pathelement location="lib/commons-collections-20040616.jar"/>
<pathelement location="lib/commons-digester-2.1.jar"/>
<pathelement location="lib/commons-beanutils-20030211.134440.jar"/>
<pathelement location="lib/struts-taglib-1.3.10.jar"/>
<pathelement location="lib/struts-1.2.9.jar"/>
<pathelement location="lib/commons-logging-1.1.1.jar"/>
<pathelement location="lib/httpcore-4.2-alpha2.jar"/>
<pathelement location="lib/httpclient-4.2-alpha1.jar"/>
<pathelement location="lib/commons-exec-1.1.jar"/>
<pathelement location="lib/guava-r09.jar"/>
<pathelement location="lib/selenium-api-2.17.0.jar"/>
<pathelement location="lib/selenium-firefox-driver-2.17.0.jar"/>
<pathelement location="lib/selenium-java-2.16.1.jar"/>
<pathelement location="lib/selenium-remote-driver-2.17.0.jar"/>
<pathelement location="lib/junit-4.10.jar"/>
<pathelement location="lib/json-20090211.jar"/>
<pathelement location="lib/servlet-api-2.5.jar"/>
<pathelement location="lib/jsp-api-2.0.jar"/>
<pathelement location="lib/log4j-1.2.16.jar"/>
</path>

<target name="compile" depends="init">
<javac srcdir="." destdir="classes">
<classpath refid="build.classpath"/>
</javac>
</target>

<path id="test.classpath">
<pathelement location="classes"/>
<pathelement location="lib/junit-4.10.jar"/>
<pathelement location="lib/selenium-api-2.17.0.jar"/>
<pathelement location="lib/selenium-firefox-driver-2.17.0.jar"/>
<pathelement location="lib/selenium-java-2.16.1.jar"/>
<pathelement location="lib/selenium-remote-driver-2.17.0.jar"/>
<pathelement location="lib/guava-r09.jar"/>
<pathelement location="lib/commons-exec-1.1.jar"/>
<pathelement location="lib/httpclient-4.2-alpha1.jar"/>
<pathelement location="lib/httpcore-4.2-alpha2.jar"/>
<pathelement location="lib/commons-logging-1.1.1.jar"/>
<pathelement location="lib/struts-1.2.9.jar"/>
<pathelement location="lib/struts-taglib-1.3.10.jar"/>
<pathelement location="lib/commons-beanutils-20030211.134440.jar"/>
<pathelement location="lib/commons-digester-2.1.jar"/>
<pathelement location="lib/commons-collections-20040616.jar"/>
<pathelement location="lib/spring-2.5.6.jar"/>
<pathelement location="lib/spring-webmvc-2.5.6.jar"/>
<pathelement location="lib/spring-web-2.5.6.jar"/>
<pathelement location="lib/hibernate3-3.6.0.Final.jar"/>
</path>

<target name="test" depends="compile" >
<junit failureproperty="junit.failure">
<test name="org.confucius.TestCalculator"/>
<classpath refid="test.classpath"/>
<formatter type="plain" usefile="false" />
</junit>
<fail if="junit.failure" message="Unit test(s) failed. See reports!"/>
</target>

<target name="dist" depends="test">
<war destfile="target/HelloWorld.war" webxml="web.xml">
<classes dir="classes"/>
<lib dir="lib">
<exclude name="jsp-api*.jar"/>
<exclude name="servlet-api*.jar"/>
</lib>
<fileset dir="web-content"/>
<webinf dir="WEB-INF"/>
</war>
<echo>Build executed at ${TIME_NOW}</echo>
</target>

<tstamp>
<format property="TIME_NOW" pattern="hh:mm:ss aa MM/dd/yyyy"/>
</tstamp>

</project>

You are now all set to use Spring MVC.

Friday, February 24, 2012

Spring-Hibernate Integration

If we carefully look at the DAO methods, we see that we do some things repeatedly - open and close session and transaction.

Why not let someone else worry about this? It will reduce chances of programing errors and reduce the code we need to write.

Spring provides a Hibernate integration which can take care of this elegantly.

You configure the Hibernate related specifications in the Spring configuration XML. Then declare the DAO as a Spring bean.

Let us see how.

In your /classes folder, create a file spring-hibernate.xml, like this:
 <beans xmlns="http://www.springframework.org/schema/beans"  
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<bean id="confuciusDS" class="org.springframework.jdbc.datasource.DriverManagerDataSource" >
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/confuciusDB"/>
<property name="username" value="confucius"/>
<property name="password" value="changeit"/>
</bean>

<bean id="confuciusSF" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="confuciusDS"/>
<property name="mappingResources">
<list>
<value>./hibernate-beans/Item.hbm.xml</value>
<value>./hibernate-beans/Cart.hbm.xml</value>
<value>./hibernate-beans/User.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>hibernate.dialect=org.hibernate.dialect.MySQLDialect</value>
</property>
</bean>

<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory">
<ref bean="confuciusSF"/>
</property>
</bean>

<import resource="spring-beans/bean-userDAO.xml" />
</beans>


This is all boiler-plate Spring-Hibernate configuration.

We have told Spring about our DataSource, how to connect to it to create a Session factory, and eventually to create HibernateTemplate - which is the only thing we will need from now.

Now we declare UserDAO as a Spring bean.
In your /classes/spring-beans/ folder, create a file bean-userDAO.xml, like this:
 <beans xmlns="http://www.springframework.org/schema/beans"  
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<bean id="userDAO" class="org.confucius.UserDAO">
<property name="hibernateTemplate">
<ref bean="hibernateTemplate"/>
</property>
</bean>
</beans>

As you can see, we are injecting HibernateTemplate into UserDAO.

Now update your UserDAO.java to contain a field heibernateTemplate, with its getters/setters so Spring can inject it.

Also, all the DAO methods will get updated to use hibernatetemplate.
Like this:
 package org.confucius;   

import java.util.List;

import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import org.springframework.orm.hibernate3.HibernateTemplate;

public class UserDAO {
private HibernateTemplate hibernateTemplate;

public void setHibernateTemplate(HibernateTemplate hibernateTemplate){
this.hibernateTemplate = hibernateTemplate;
}

public HibernateTemplate getHibernateTemplate(){
return hibernateTemplate;
}

// Criteria with Restrictions
public List<User> getUsers (String firstName, String lastName) throws Exception{
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(User.class)
.add(Restrictions.eq("firstName", firstName))
.add(Restrictions.eq("lastName", lastName));

return ((List<User>) hibernateTemplate.findByCriteria(detachedCriteria));
}


// HQL with parameters
public List<User> getUsers (String firstName) throws Exception{
return ((List<User>) hibernateTemplate.find("from User where firstName=?", firstName));
}

// HQL with no parameters
public List<User> getUsers () throws Exception{
return ((List<User>) hibernateTemplate.find("from User"));
}


// Getting an object given its Id
public User getUser (long userId) throws Exception{
return ((User) hibernateTemplate.find("from User where id=?", userId));
}


// Persist
public void persist (User user) throws Exception{
hibernateTemplate.persist(user);
}
}


As you can see, our DAO methods have become considerable more simple, literally one-liners!

This is because Spring is taking care of Sessions and Transactions.

Update your TestPersistence.java to use the new UserDAO, like this:
 package org.confucius;   

import java.util.Iterator;
import java.util.List;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class TestPersistence {

public static void main(String[] args) {
try {
ApplicationContext springContext = new ClassPathXmlApplicationContext("spring-hibernate.xml");
UserDAO userDAO = (UserDAO) springContext.getBean("userDAO");

List<User> users = userDAO.getUsers("Joe", "Trumpet");
for (Iterator<User> iter = users.iterator(); iter.hasNext();)
System.out.println(iter.next());

} catch (Exception e) {
e.printStackTrace();
}
}
}

R-click on TestPersistence.java in Eclipse Navigator view.
Select Run As -> Java Application.

You will see Joe Trumpet printed to the console, along with contents of his cart.

Note, I had to use Hibernate3 (instead of Hibernate4) because of Spring issues with Hibernate4. I also needed a few other jars.

My ivy.xml now looks like this:

 <ivy-module version="2.0">  
<info organisation="org.confucius" module="helloworld"/>
<dependencies>
<dependency org="org.springframework" name="spring" rev="2.5.6"/>
<dependency org="commons-pool" name="commons-pool" rev="20030825.183949"/>
<dependency org="commons-dbcp" name="commons-dbcp" rev="20030825.184428"/>
<dependency org="antlr" name="antlr" rev="20030911"/>
<dependency org="javassist" name="javassist" rev="3.12.1.GA"/>
<dependency org="mysql" name="mysql-connector-java" rev="5.1.18"/>
<dependency org="javax.persistence" name="persistence-api" rev="1.0.2"/>
<dependency org="jboss" name="jboss-j2ee" rev="4.2.2.GA"/>
<dependency org="org.jboss.logging" name="jboss-logging" rev="3.1.0.GA"/>
<dependency org="org.slf4j" name="slf4j-simple" rev="1.6.1"/>
<dependency org="org.slf4j" name="slf4j-api" rev="1.6.4"/>
<dependency org="org.hibernate" name="hibernate3" rev="3.6.0.Final"/>
<dependency org="dom4j" name="dom4j" rev="1.6.1"/>
<dependency org="struts" name="struts" rev="1.2.9"/>
<dependency org="org.apache.struts" name="struts-taglib" rev="1.3.10"/>
<dependency org="commons-collections" name="commons-collections" rev="20040616"/>
<dependency org="commons-digester" name="commons-digester" rev="2.1"/>
<dependency org="commons-beanutils" name="commons-beanutils" rev="20030211.134440"/>
<dependency org="commons-logging" name="commons-logging" rev="1.1.1"/>
<dependency org="org.apache.httpcomponents" name="httpcore" rev="4.2-alpha2"/>
<dependency org="org.apache.httpcomponents" name="httpclient" rev="4.2-alpha1"/>
<dependency org="org.apache.commons" name="commons-exec" rev="1.1"/>
<dependency org="com.google.guava" name="guava" rev="r09"/>
<dependency org="org.seleniumhq.selenium" name="selenium-api" rev="2.17.0"/>
<dependency org="org.seleniumhq.selenium" name="selenium-remote-driver" rev="2.17.0"/>
<dependency org="org.seleniumhq.selenium" name="selenium-firefox-driver" rev="2.17.0"/>
<dependency org="org.seleniumhq.selenium" name="selenium-java" rev="2.16.1"/>
<dependency org="junit" name="junit" rev="4.10"/>
<dependency org="org.json" name="json" rev="20090211"/>
<dependency org="javax.servlet" name="servlet-api" rev="2.5"/>
<dependency org="javax.servlet" name="jsp-api" rev="2.0"/>
<dependency org="jstl" name="jstl" rev="1.2"/>
<dependency org="log4j" name="log4j" rev="1.2.16"/>
</dependencies>
</ivy-module>

Thursday, February 23, 2012

Hibernate - Criteria API

Criteria API is the preferred way of doing Queries in Hibernate.

Let us write a DAO method which uses Criteria API to get Users by their first and last names.

Update UserDAO.java, like this:
 package org.confucius;   

import java.util.ArrayList;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Restrictions;

public class UserDAO {

// Criteria with Restrictions
public static List<User> getUsers (String firstName, String lastName) throws Exception{
List<User> users = new ArrayList<User>();
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();

try {
Criteria criteria = session.createCriteria(User.class);

criteria.add(Restrictions.eq("firstName", firstName));
criteria.add(Restrictions.eq("lastName", lastName));

users = (List<User>) criteria.list();

tx.commit();
}
catch (Exception e){
tx.rollback();
throw e;
}

session.close();
return users;
}


// HQL with parameters
public static List<User> getUsers (String firstName) throws Exception{
List<User> users = new ArrayList<User>();
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();

try {
Query query = session.createQuery("from User where firstName = :firstName");
query.setParameter("firstName", firstName);
users = (List<User>) query.list();
tx.commit();
}
catch (Exception e){
tx.rollback();
throw e;
}

session.close();
return users;
}

// HQL with no parameters
public static List<User> getUsers () throws Exception{
List<User> users = new ArrayList<User>();
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();

try {
Query query = session.createQuery("from User");
users = (List<User>) query.list();
tx.commit();
}
catch (Exception e){
tx.rollback();
throw e;
}

session.close();
return users;
}


// Getting an object given its Id
public static User getUser (long userId) throws Exception{
User user = null;
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();

try {
user = (User) session.get(User.class, userId);
tx.commit();
}
catch (Exception e){
tx.rollback();
throw e;
}

session.close();
return user;
}


// Persist
public static void persist (User user) throws Exception{
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();

try {
session.persist(user);
tx.commit();

} catch (Exception e){
tx.rollback();
throw e;
}

session.close();
}
}

Update your TestPersistence.java to use this new DAO method, like this:
 package org.confucius;   

import java.util.Iterator;
import java.util.List;


public class TestPersistence {

public static void main(String[] args) {
try {
List<User> users = UserDAO.getUsers("Joe", "Trumpet");
for (Iterator<User> iter = users.iterator(); iter.hasNext();)
System.out.println(iter.next());

} catch (Exception e) {
e.printStackTrace();
}
}
}


R-click on the TestPersistence.java file in your Eclipse Navigator view.
Select Run As->Java Application

You will see Joe Trumpet and details about his Cart printed to the console.

Hibernate - HQL with Parameters

Let us now parametrize our HQL query, so we can get Users by their firstName.

Update your UserDAO, like this:
 package org.confucius;   

import java.util.ArrayList;
import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;

public class UserDAO {

// HQL with parameters
public static List<User> getUsers (String firstName) throws Exception{
List<User> users = new ArrayList<User>();
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();

try {
Query query = session.createQuery("from User where firstName = :firstName");
query.setParameter("firstName", firstName);
users = (List<User>) query.list();
tx.commit();
}
catch (Exception e){
tx.rollback();
throw e;
}

session.close();
return users;
}

// HQL with no parameters
public static List<User> getUsers () throws Exception{
List<User> users = new ArrayList<User>();
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();

try {
Query query = session.createQuery("from User");
users = (List<User>) query.list();
tx.commit();
}
catch (Exception e){
tx.rollback();
throw e;
}

session.close();
return users;
}


// Getting an object given its Id
public static User getUser (long userId) throws Exception{
User user = null;
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();

try {
user = (User) session.get(User.class, userId);
tx.commit();
}
catch (Exception e){
tx.rollback();
throw e;
}

session.close();
return user;
}


// Persist
public static void persist (User user) throws Exception{
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();

try {
session.persist(user);
tx.commit();

} catch (Exception e){
tx.rollback();
throw e;
}

session.close();
}
}

Update your TstPersistence.java to use this new DAO method, like this:
 package org.confucius;   

import java.util.Iterator;
import java.util.List;


public class TestPersistence {

public static void main(String[] args) {
try {
List<User> users = UserDAO.getUsers("Joe");
for (Iterator<User> iter = users.iterator(); iter.hasNext();)
System.out.println(iter.next());

} catch (Exception e) {
e.printStackTrace();
}
}
}

R-click on the TestPersistence.java file in your Eclipse Navigator view.
Select Run As->Java Application

You will see Joe Trumpet and details about his Cart printed to the console.

Wednesday, February 22, 2012

Hibernate - HQL

HQL --> Hibernate Query Language

SQL queries have slight differences between different databases.

Hibernate has a SQL version called HQL - which closely mimics the SQL that you are used to. Behind the scenes Hibernate generates the correct SQL queries based on the database.

Let us update UserDAO to use HQL to get a list of Users from the database.

Your UserDAO.java will now look like this:
 package org.confucius;   

import java.util.ArrayList;
import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;

public class UserDAO {

// HQL with no parameters
public static List<User> getUsers () throws Exception{
List<User> users = new ArrayList<User>();
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();

try {
Query query = session.createQuery("from User");
users = (List<User>) query.list();
tx.commit();
}
catch (Exception e){
tx.rollback();
throw e;
}

session.close();
return users;
}


// Getting an object given its Id
public static User getUser (long userId) throws Exception{
User user = null;
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();

try {
user = (User) session.get(User.class, userId);
tx.commit();
}
catch (Exception e){
tx.rollback();
throw e;
}

session.close();
return user;
}


// Persist
public static void persist (User user) throws Exception{
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();

try {
session.persist(user);
tx.commit();

} catch (Exception e){
tx.rollback();
throw e;
}

session.close();
}
}


The HQL query "form User" is, in principle, equivalent to the SQL query "select from user". But Hibernate will generate all the SQL queries needed to get the Users, their Carts and the Items in the cart.

So HQL queries pack a lot of punch. In addition, they are optimized for the Database they are working against.


Update your TestPersistence.java to use this new HQL based DAO method:
 package org.confucius;   

import java.util.Iterator;
import java.util.List;


public class TestPersistence {

public static void main(String[] args) {
try {
List<User> users = UserDAO.getUsers();
for (Iterator<User> iter = users.iterator(); iter.hasNext();)
System.out.println(iter.next());

} catch (Exception e) {
e.printStackTrace();
}
}
}

R-click on the TestPersistence.java file in your Eclipse Navigator view.
Select Run As->Java Application.

You will see the user Joe printed to the console, including his cart and its items.

Note the SQL queries that Hibernate generated : the HQL "from User" query resulted in multiple SQL queries.

Hibernate - Querying

So far we saw how to persist objects to the database.

Now let us see how to get them back.

There are 3 ways to do this in Hibernate:
- To get objects directly by their Id, we can use the Session
- If we want to use SQL-like queries, we can use HQL (we will see this later)
- The preferred way is to use Hibernate Criteria API (we will see this later).

First, let us look at the simplest way - to get Objects by their Id.

Update your UserDAO.java with a method to get the User by Id, like this:
 package org.confucius;   

import org.hibernate.Session;
import org.hibernate.Transaction;

public class UserDAO {

// Getting an object given its Id
public static User getUser (long userId) throws Exception{
User user = null;
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();

try {
user = (User) session.get(User.class, userId);
tx.commit();
}
catch (Exception e){
tx.rollback();
throw e;
}

session.close();
return user;
}


// Persist
public static void persist (User user) throws Exception{
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();

try {
session.persist(user);
tx.commit();

} catch (Exception e){
tx.rollback();
throw e;
}

session.close();
}
}

We have used Session.get() to get the User.

Next, let us update User.java with a toString() method, so we can print the User to console.

Your User.java will now look like this:
 package org.confucius;   

import java.util.Iterator;

public class User {
private long id;
private String firstName;
private String lastName;
private Cart cart;

public void setId(long id) {
this.id = id;
}

public long getId() {
return id;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getFirstName() {
return firstName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public String getLastName() {
return lastName;
}

public Cart getCart() {
return cart;
}

public void setCart(Cart cart) {
this.cart = cart;
}

public String toString(){
StringBuffer userStr = new StringBuffer();

userStr.append(firstName + " " + lastName + "\n");
userStr.append("Number of Items in Cart = " + cart.getItems().size() + "\n");

for (Iterator<Item> iter = cart.getItems().iterator(); iter.hasNext();)
userStr.append("\tItem = " + iter.next().getName() + "\n");

return userStr.toString();
}

}

Next, update your TestPersistence.java to get a User from the database, like this:
 package org.confucius;   

public class TestPersistence {

public static void main(String[] args) {
try {
User joe = UserDAO.getUser(1);
System.out.println(joe);

} catch (Exception e) {
e.printStackTrace();
}
}
}


Now we need to do a few updates to the Hibernate mappings. We need to turn off the "lazy" loading.

By default, Hibernate will not get the dependencies of an object. It will get them only if and when they are needed (lazy).

Update your User.hbm.xml, like this:
 <?xml version="1.0"?>   
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
<class name="org.confucius.User">
<id name="id" type="long">
<generator class="identity" />
</id>
<property name="firstName" type="string" />
<property name="lastName" type="string" />
<many-to-one name="cart" unique="true" cascade="all" lazy="false"/>
</class>
</hibernate-mapping>

Update Cart.hbm.xml, like this:
 <?xml version="1.0"?>   
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
<class name="org.confucius.Cart">
<id name="id" type="long">
<generator class="identity" />
</id>
<list name="items" cascade="all" lazy="false">
<key column="cartId"/>
<list-index column="cartListIndex"/>
<one-to-many class="org.confucius.Item"/>
</list>
</class>
</hibernate-mapping>

Finally, we need to update the hibernate.cfg.xml to prevent it from re-creating the tables (we need the data which is in there!).
Like this:
 <?xml version="1.0" encoding="utf-8"?>   
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/confuciusDB</property>
<property name="hibernate.connection.username">confucius</property>
<property name="hibernate.connection.password">changeit</property>
<property name="hibernate.hbm2ddl.auto">update</property>
<property name="show_sql">true</property>

<mapping resource="hibernate-beans/Item.hbm.xml"></mapping>
<mapping resource="hibernate-beans/Cart.hbm.xml"></mapping>
<mapping resource="hibernate-beans/User.hbm.xml"></mapping>
</session-factory>
</hibernate-configuration>

R-click on the TestPerssitence.java file in Eclipse Navigator view.
Select Run As--> Java Application

You will see user Joe printed to the console, along with the contents of his cart.

Notice how Hibernate generated all the SQL queries needed to get the User, Cart and Items.

Tuesday, February 21, 2012

Hibernate - List Mapping

To make our example more realistic, let us make Cart contain a List of Items.
Then see how to map this List of Items in Hibernate.

In your /src/org/confucius, create a class Item.java, like this:
 package org.confucius;  

public class Item {
private long id;
private String name;

public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}


Now, update your Cart.java to contain a List of Items. Like this:
 package org.confucius;  

import java.util.List;

public class Cart {
private long id;
private List<Item> items;

public long getId() {
return id;
}

public void setId(long id) {
this.id = id;
}

public List<Item> getItems() {
return items;
}

public void setItems(List<Item> items) {
this.items = items;
}
}

In your /classes folder, create a file Item.hbm.xml, like this:
 <?xml version="1.0"?>   
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
<class name="org.confucius.Item">
<id name="id" type="long">
<generator class="identity" />
</id>
<property name="name" type="string" />
</class>
</hibernate-mapping>

Nothing new here. We map Item to Hibernate.

Update your hibernate.cfg.xml to make it aware of the Item mapping, like this:
 <?xml version="1.0" encoding="utf-8"?>   
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/confuciusDB</property>
<property name="hibernate.connection.username">confucius</property>
<property name="hibernate.connection.password">changeit</property>
<property name="hibernate.hbm2ddl.auto">create</property>
<property name="show_sql">true</property>

<mapping resource="hibernate-beans/Item.hbm.xml"></mapping>
<mapping resource="hibernate-beans/Cart.hbm.xml"></mapping>
<mapping resource="hibernate-beans/User.hbm.xml"></mapping>
</session-factory>
</hibernate-configuration>

Note that we have added "show-sql" so we can see what Hibernate does behind the scenes.


Now, update your Cart.hbm.xml to map the List of Items, like this:
 <?xml version="1.0"?>   
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
<class name="org.confucius.Cart">
<id name="id" type="long">
<generator class="identity" />
</id>
<list name="items" cascade="all">
<key column="cartId"/>
<list-index column="cartListIndex"/>
<one-to-many class="org.confucius.Item"/>
</list>
</class>
</hibernate-mapping>

We have declared a List of Items, and specified the column names for the Key and List-Index ...what these mean will become clear later.

Finally, update TestPersistence.java, like this:
 package org.confucius;   

import java.util.ArrayList;
import java.util.List;


public class TestPersistence {

public static void main(String[] args) {
User joe = new User();
joe.setFirstName("Joe");
joe.setLastName("Trumpet");

Cart cart = new Cart();
joe.setCart(cart);

List<Item> items = new ArrayList<Item> ();
cart.setItems(items);

Item toothpaste = new Item();
toothpaste.setName("toothpaste");
items.add(toothpaste);

Item diapers = new Item();
diapers.setName("diapers");
items.add(diapers);

Item detergent = new Item();
detergent.setName("detergent");
items.add(detergent);

try {
UserDAO.persist(joe);
} catch (Exception e) {
e.printStackTrace();
}
}
}


R-click on TestPersistence.java in Eclipse Navigator view.
Select Run As->Java Application.

Notice that Hibernate displays the SQL queries in the console because we set the show-sql to true.


If you look at the confuciusDB, you will see the three tables - user, cart and item - linked to each other through foreign keys.
 mysql> select * from user;  
+----+-----------+----------+------+
| id | firstName | lastName | cart |
+----+-----------+----------+------+
| 1 | Joe | Trumpet | 1 |
+----+-----------+----------+------+
1 row in set (0.00 sec)

mysql> select * from cart;
+----+
| id |
+----+
| 1 |
+----+
1 row in set (0.00 sec)

mysql> select * from item;
+----+------------+--------+---------------+
| id | name | cartId | cartListIndex |
+----+------------+--------+---------------+
| 1 | toothpaste | 1 | 0 |
| 2 | diapers | 1 | 1 |
| 3 | detergent | 1 | 2 |
+----+------------+--------+---------------+
3 rows in set (0.00 sec)

You will see how the key and list-index columns we specified in Cart mapping are used in the item table.

Note that the cartListIndex columns contains the exact sequence in which each item was added to the items list.