Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Scrollbar

...

So, you fill in all the fields, submit the form (without validation errors) and voila: you get back the same form, blanked out. What happened, and where did the data go?

...

Well, since we're creating objects, we might as well store them somewhere ... in a database. We're going to quickly integrate Tapestry with Hibernate as the object/relational mapping layer, and ultimately store our data inside a HyperSQL (HSQLDB) database. HSQLDB is an embedded database engine and requires no installation – it will be pulled down as a dependency via mavenby Maven.

Re-configuring the Project

...

First, we must update the POM to list a new set of dependencies, that includes Hibernate, the Tapestry/Hibernate integration library, and the HSQLDB JDBC driver:

Code Block
XML
languageXMLxml
titlesrc/pom.xml (partial)

    <dependencies>

        <dependency>
            <groupId>org.apache.tapestry</groupId>
            <artifactId>tapestry-hibernate</artifactId>
            <version>${tapestry-release-version}</version>
        </dependency>

        <dependency>
            <groupId>hsqldb<<groupId>org.hsqldb</groupId>
            <artifactId>hsqldb</artifactId>
            <version>1<version>2.83.0.7<2</version>
        </dependency>
        ...
    </dependencies>

The tapestry-hibernate library includes, as transitive dependencies, Hibernate and tapestry-core. This means that you can simply replace "tapestry-core" with "tapestry-hibernate" inside the <artifactId> element.Since Hibernate can work with so many different databases, we must explicitly add in the correct driver.

After changing the POM , you must re-execute the command mvn eclipse:eclipse -DdownloadSources=true. Then, inside Eclipse, right click on the project (in Package Explorer) and select the "Refresh" menu item. You should also stop Jettyand saving, Maven should automatically download the JARs for the new dependencies.

Hibernate Configuration

Hibernate has needs a master configuration file, hibernate.cfg.xml, used to store connection and other data. Create this in your src/main/resources folder:

Code Block
XML
languageXMLxml
titlesrc/main/resources/hibernate.cfg.xml

<!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.connection.driver_class">org.hsqldb.jdbcDriver</property>
        <property name="hibernate.connection.url">jdbc:hsqldb:./target/work/t5_tutorial1;shutdown=true</property>
        <property name="hibernate.dialect">org.hibernate.dialect.HSQLDialect</property>
        <property name="hibernate.connection.username">sa</property>
        <property name="hibernate.connection.password"></property>
        <property name="hbm2ddl.auto">update</property>
        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.format_sql">true</property>
    </session-factory>
</hibernate-configuration>

...

But what entities? Normally, the available entities are listed inside hibernate.cfg.xml, but that's not necessary with Tapestry; in another example of convention over configuration, Tapestry locates all entity classes inside the entities package ("com.example.tutorial1.entities" in our case) and adds them to the configuration. Currently, that is just the Address entity.

...

Below is the updated Address class, with the Hibernate annotations (as well as the Tapestry ones).

Code Block
languagejava
titlesrc/main/java/com/example/tutorial/entities/Address.java

package com.example.tutorialtutorial1.entities;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

import org.apache.tapestry5.beaneditor.NonVisual;
import org.apache.tapestry5.beaneditor.Validate;

import com.example.tutorialtutorial1.data.Honorific;

@Entity
public class Address
{
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @NonVisual
  public Long id;

  public Honorific honorific;

  @Validate("required")
  public String firstName;

  @Validate("required")
  public String lastName;

  public String street1;

  public String street2;

  @Validate("required")
  public String city;

  @Validate("required")
  public String state;

  @Validate("required,regexp")
  public String zip;

  public String email;

  public String phone;
}

...

  • @NonVisual – indicates a field, such as a primary key, that should not be made visible to the user.
  • @Validate – identifies the validations associated with a field.

At this point you should stop and restart your application.

Updating the Database

So we have a database set up and running, and Hibernate is configured to connect to it. Let's make use of that to store our Address object in the database.

What we need is to provide some code to be executed when the form is submitted. When a Tapestry form is submitted, there is a whole series of events that get fired. The event we are interested in is the "success" event, which comes late in the process, after all the values have been pulled out of the request and applied to the page properties, and after all server-side validations have occuredoccurred.

The success event is only fired if there are no validation errors.

...

  • Use the Hibernate Session object to persist the new Address object.
  • Commit the transaction to force the data to be written to the database.

Let's update our CreateAddress.java class:

Code Block
languagejava
titlesrc/main/java/orgcom/apache/tapestry5example/tutorial/pages/address/CreateAddress.java

package orgcom.apacheexample.tapestry5.tutorialtutorial1.pages.address;

import orgcom.apacheexample.tapestry5tutorial1.annotationsentities.InjectPageAddress;
import orgcom.apacheexample.tapestry5tutorial1.annotationspages.PropertyIndex;
import org.apache.tapestry5.hibernate.annotations.CommitAfterInjectPage;
import org.apache.tapestry5.ioc.annotations.InjectProperty;
import org.apache.tapestry5.tutorialhibernate.entitiesannotations.AddressCommitAfter;
import org.apache.tapestry5.tutorialioc.pagesannotations.IndexInject;
import org.hibernate.Session;

public class CreateAddress
{
    @Property
    private Address address;

    @Inject
    private Session session;

    @InjectPage
    private Index index;

    @CommitAfter
    Object onSuccess()
    {
        session.persist(address);

        return index;
    }
}

...

Tapestry automatically starts a transaction as necessary; however that transaction will be aborted at the end of the request by default. If we make changes to persistent objects, such as adding a new Address object, then it is necessary to commit the transaction.

...

As a little preview of what's next, let's display all the Addresses entered by the user on the Index page of the application. After you enter a few names, it will look something like:

Image Modified

Adding the Grid to the Index page

...

A minimal Grid is very easy to add to the template. Just add this near the bottom of Index.tml:

Code Block
XML
languageXMLxml
titlesrc/main/webapp/Index.tml (partial)

  <t:grid source="addresses"
         include="honorific,firstName,lastName,street1,city,state,zip,phone"/>

Note that the Grid component accepts many of the same parameters that we used with the BeanEditForm. Here we use the include parameter to specify the properties to show, and in what order.

Now And all we have to do is supply the addresses property in the Java code. Here's how Index.java should look now:

Code Block
languagejava
titlesrc/main/java/orgcom/apache/tapestry5example/tutorial/pages/Index.java (partial)
package com.example.tutorial1.pages;
import java.util.List;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.hibernate.Session;
import com.example.tutorial1.entities.Address;
public class Index
{
    @Inject
    private Session session;

    public List<Address> getAddresses()
    {
        return session.createCriteria(Address.class).list();
    }
}

Here, we're using the Hibernate Session object to find all Address objects in the database. Any sorting that takes place will be done in memory. This is fine for now (with only a handful of Address objects in the database). Later we'll see how to optimize this for very large result sets.

...

We have lots more to talk about: more components, more customizations, built-in Ajax support, more common design and implementation patterns, and even writing your own components (which is easy!).More tutorials are planned, but for now check

...

...


Scrollbar

...