HIBERNATE  |  Register  | 
      SEARCH: 
   
News 
Features 
Documentation 
   Related Projects 
   External Documentation 
Download 
Forum & Mailinglists 
Support 
JIRA Issue Tracking
Wiki Community Area


JBoss.org



middlegen

JIRA Issue Tracking




      
Documentation > Community Area > A simple Hibernate + Tapestry application

A simple Hibernate + Tapestry application

Document purpose: making a simple Tapestry and Hibernate application to work.

Links:

- full document/updated

- send comments

- Hibernate(the DB persistence)

- Tapestry (the Web framework)

- me, Nemesis IT (my company)

Application: dummy application containing just two tables (contact, user)

Download: download the code (the libraries are missing to have a smaller archive, you need the ones for hibernate and tapestry (including contrib))

Updates to this version (12 oct 03):

  • Added HibernateGlobal method of using Hibernate
  • Changed login method using callbacks mechanism
  • Changed the ugly green with a more supportable blue
  • Moved Hibernate config to hibernate.cfg.xml
  • Added the AppPage base page including Hibernate and Validation functionality
  • switched to Tapestry 3.0beta3

Changes from the previous version:

  • change

<!DOCTYPE page-specification

PUBLIC "-//Howard Lewis Ship//Tapestry Specification 1.3//EN"

"http://tapestry.sf.net/dtd/Tapestry_1_3.dtd"> to

<!DOCTYPE page-specification PUBLIC

"-//Apache Software Foundation//Tapestry Specification 3.0//EN"

"http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd">

and similar ones

  • change parameters to new names
  • move parts from .jwc and .page files to the HTML template (ex: jwcid="@Form" instead of defining a form component in the .page). This tends to simplify things.
  • update code to use org.apache.tapestry instead of net.sf.tapestry and new methods names such as getPageName() instead of getName()
  • update everything to replace /net/sf/tapestry with /org/apache/tapestry
  • remove the RequestCycleException which does not seem to exist anymore

Library versions: Hibernate 2.0, Tapestry 3.0beta3

1. Start with the hibernate mapping files (contact.hdb.xml and user.hdb.xml) for the database structure and the hibernate.properties (I am using a postgresql database).

<class name="ro.nit.contacts.data.Contact" table="nitc_contact">
<id name="id" column="contact_id" type="long">
<generator class="native"/>
</id>

<property name="name" type="string" not-null="true" length="128"/>
<property name="data" type="string" length="512"/>
<property name="company" type="string" not-null="true" length="128"/>
<property name="status" type="string" not-null="true" length="64"/>
<property name="verifyDate" type="java.sql.Date" not-null="true"/>
<property name="note" type="string" length="512"/>
<property name="estimate" type="int"/>
<many-to-one
name="user"
class="ro.nit.contacts.data.User"
not-null="true">
<column name="user_id" />
</many-to-one>

</class>

<class name="ro.nit.contacts.data.User" table="nitc_user">
<id name="id" column="user_id" type="long">
<generator class="native"/>
</id>

<property name="name" type="string" not-null="true" length="128"/>
<property name="pass" type="string" not-null="true" length="128"/>
<property name="email" type="string" not-null="true" length="128"/>
<property name="data" type="string" length="512"/>

<set
name="contacts"
lazy="true"
inverse="true"
>
<key>
<column name="user_id" />
</key>
<one-to-many class="ro.nit.contacts.data.Contact"/>
</set>
</class>

2. Tapestry application

<application name="contacts" engine-class="ro.nit.contacts.AppEngine" >

<property name="org.apache.tapestry.visit-class">ro.nit.contacts.Visit</property>
<page name="Home" specification-path="/ro/nit/contacts/Home.page"/>
<page name="Add" specification-path="/ro/nit/contacts/Add.page"/>
<page name="Update" specification-path="/ro/nit/contacts/Add.page"/> (same page, different name)
<page name="View" specification-path="/ro/nit/contacts/View.page"/>
<page name="Logon" specification-path="/ro/nit/contacts/Logon.page"/> (added logon page)

<component-alias type="ShowError" specification-path="/ro/nit/components/ShowError.jwc" />
<component-alias type="Menu" specification-path="/ro/nit/components/Menu.jwc" />
<component-alias type="LogonComp" specification-path="/ro/nit/components/LogonComp.jwc" /> (logon form)
<component-alias type="Author" specification-path="/ro/nit/components/Author.jwc" /> (author text at the bottom right)

<library id="contrib" specification-path="/org/apache/tapestry/contrib/Contrib.library" />

</application>

3. The HUtil class is going to contain hibernate code to obtain a Session object

try {
Configuration cfg = new Configuration();
cfg.
addClass(Contact.class).
addClass(User.class);

Properties properties=new Properties();
properties.load(HUtil.class.getResourceAsStream("/hibernate.properties"));
cfg.setProperties(properties);

sessionFactory=cfg.buildSessionFactory();
} catch (Exception e) {
e.printStackTrace();
}

4. The Visit object contains authentification code

try {
Session sess = HUtil.getSession();
Query q = sess.createQuery("select u from u in class ro.nit.contacts.data.User where u.name=:name and u.pass=:pass");
q.setParameter("name",username);
q.setParameter("pass",password);
List result = q.list();
if(!result.isEmpty()){
user = (User)result.iterator().next();
password = null;
return true;
}
sess.close();
} catch (HibernateException e) {
e.printStackTrace();
}
return false;

5. The Login page is just a simple form for authentication

Securing the application only require for each page to extend the SecuredPage class

public class SecuredPage extends BasePage{
public void validate(IRequestCycle iRequestCycle){
Visit v = (Visit)getVisit();
if(!v.isAuthenticated()){
throw new PageRedirectException("Logon");
}
super.validate(iRequestCycle);
}

public void detach(){
super.detach();
}
}

6. The Home page does not contains nothing

Graphic 1:Home page

7. The Add page contains logic for adding a contact with validation. I use some ValidField components for checking the input entered by the user. This validation is one of the most ugly things I can thing about when creating some dynamic pages. This is mainly due to the repetitive, error generating code.

<form jwcid="@Form" delegate="ognl:beans.delegate" listener="ognl:listeners.formSubmit">
<span jwcid="@ShowError" delegate="ognl:beans.delegate"/>
<table cellpadding="4">

<!--
<tr><td>Notes:</td><td><textarea jwcid="notesText" cols="20" rows="5"/></td></tr>
-->

<tr class="row1">
<td><span jwcid="@FieldLabel" field="ognl:components.nameField">Name:</span></td>
<td><input jwcid="nameField" size="20"/></td>
<td><span jwcid="@FieldLabel" field="ognl:components.companyField">Company:</span></td>
<td><input jwcid="companyField" size="20"/></td>
</tr>
<tr class="row1">
<td>Verify Date:</td>
<td><span jwcid="@DatePicker" value="ognl:contact.verifyDate"/></td>
<td>Status:</td>
<td><span jwcid="@PropertySelection" model="ognl:@ro.nit.contacts.Add@STATUS" value="ognl:contact.status"/></td>
</tr>
<tr class="row1">
<td>Data:</td>
<td><input jwcid="@TextArea" value="ognl:contact.data" cols="20"/></td>
<td>Note:</td>
<td><input jwcid="@TextArea" value="ognl:contact.note" cols="20"/></td>
</tr>
<tr class="row1">
<td>&nbsp;</td>
<td>&nbsp;</td>
<td><span jwcid="@FieldLabel" field="ognl:components.estimateField">Estimate:</span></td>
<td><input jwcid="estimateField" size="3"/>(1-10 value)</td>
</tr>

<tr align="right">
<td colspan="4"><input type="submit" jwcid="@Submit" label="ognl:page.pageName"/></td> (change the button label based on the page name)
</tr>
</table>
</form>
public void activateExternalPage(Object[] objects, IRequestCycle cycle) {
if(objects!=null&&objects.length>=1){
setContact((Contact)objects[0]);
}
}


public void formSubmit(IRequestCycle cycle) {

ValidationDelegate delegate = (ValidationDelegate)
getBeans().getBean("delegate");

// If no errors process the bid, otherwise stay on this page and
// let the fields show their errors.
if (!delegate.getHasErrors()){
try {
Session sess = null;
Transaction tx = null;
try {
sess = HUtil.getSession();
tx = sess.beginTransaction();
contact.setUser(((Visit)getVisit()).getUser());

if("Add".equalsIgnoreCase(getPageName())) (save or update the contact based on how the page was called)
sess.save(contact);
else
sess.update(contact);

tx.commit();
cycle.activate("View");
} catch (Exception e) {
e.printStackTrace();
tx.rollback();
} finally {
if(sess!=null){
sess.flush();
sess.close();
}
}
} catch (HibernateException e) {
e.printStackTrace();
}
contact = new Contact();
}
} 

Observation: maybe a generator could be written. This will take a mapping file and output a component for entering data.

Graphic 2:Add page

Graphic 3:Add page (incomplete data)

Graphic 4:Add page called as Update

6. The View page contains just a table with data (I stoped using the table component since it seemed much to complicated for this case, instead I used a simple foreach)

Graphic 4:View page (the table)

<table width="600" border="1" cellpadding="1" cellspacing="0" bordercolor="#999999" style="BORDER-COLLAPSE: collapse">
<tr class="rowh">
<td>Name</td>
<td>Company</td>
<td>Status</td>
<td>Data</td>
<td>Verify Date</td>
<td>Note</td>
<td>Estimate</td>
<td>&nbsp;</td>
</tr> <tr jwcid="@Foreach" source="ognl:contacts" value="ognl:contact" element="tr" class="ognl:cssClass">
<td><span jwcid="@Insert" value="ognl:contact.name"/></td>
<td><span jwcid="@Insert" value="ognl:contact.company"/></td>
<td><span jwcid="@Insert" value="ognl:contact.status"/></td>
<td><span jwcid="@Insert" value="ognl:contact.data"/></td>
<td><span jwcid="@Insert" value="ognl:contact.verifyDate"/></td>
<td><span jwcid="@Insert" value="ognl:contact.note"/></td>
<td><span jwcid="@Insert" value="ognl:contact.estimate"/></td>
<td><span jwcid="@ExternalLink" page="Update" parameters="ognl:contact">Update</span></td>
</tr>
</table>

<span jwcid="@Author"/>

public List getContacts(){
Session sess = null;
try {
sess = HUtil.getSession();
List results = sess.find("from c in class ro.nit.contacts.data.Contact");
return results;
} catch (Exception e) {
e.printStackTrace();
}finally{
if(sess!=null)
try {
sess.close();
} catch (Exception e) {
e.printStackTrace();
//conceal this for now
}
}
return null;
} 
      

coWiki web collaboration