Creating a Database-driven Application"

From Documentation
m
Line 1: Line 1:
 +
{{template:MainMenu}}
 
=Before You Start=
 
=Before You Start=
  

Revision as of 04:27, 6 December 2011


Before You Start

Translations are available in English, Français, Español, Italiano and 日本語.

Other Introductions

A database-driven real-world application

We will show you, step by step, how to develop a simple Web application that uses a database. Although this tutorial is intended for new users of ZK, it is suggested that users must have at least a a basic knowledge about Java. Do not feel panic if you do not have any knowledge of other Web development languages, Java is all you need to know in order to develop Ajax-enabled Web applications with ZK.

In this tutorial, we assume that you have already installed JDK (1.4 or above), and a Servlet container (e.g., Tomcat). For more information, please take a look at Creating Hello World with Eclipse and ZK Studio.

Your first ZK application "To-do" list

Imagine that in order to plan our day better, we need an application which stores events we are going to deal with in the future. Such a Web application would require the use of a database. For the purposes of this tutorial, we'll use the Java database (HSQL DB) which does not require us to install a database server. You could try this tutorial application [1].

Download the ZK Todo file

  1. Download zktodo-0.9.2.zip
  2. Unzip downloaded archive, which contains the following todo.war, hsqldb folder, and a readme.txt.

Running the sample application without an IDE

  1. Copy the hsqldb folder to the root directory where you installed the Apache Tomcat. (ex.C:\)
  2. Copy todo.war to the $TOMCAT_HOME\webapps directory.
  3. Start Apache Tomcat.
  4. Open your browser and navigate to http://localhost:8080/todo.

Running the sample application with an IDE

  1. Copy hsqldb folder to the root directory where your eclipse workspace located. (ex.C:\).
  2. Activate Eclipse.
  3. Select File > Import .
  4. In the Import dialog, select Web > WAR file and then click Next.
  5. Using the Browse button to locate todo.war.
  6. Click Finish to import the Web Project.
  7. Right click on todo project in the explorer and select Run As > Run on Server
  8. Select Apache > Tomcat v6.0 Server in the server type dialog and click Finish
  9. A browser will be activated automatically to explore the todo example.

Todo.png

All Scenarios

  • Key in related information about an event, and press the Add button to insert it into database.
  • Select any row in the table to show event information in the fields below for users to modify, then press Update button to update the event.
  • Select any row in the table, and press the Delete button to delete the selected "Event".

Model

In the following paragraphs, the database schema, domain object, and the DAO object will be introduced.

DataBase Schema

A database table which holds our application's data needs the following attributes: event id, event name, event priority and event date.

The database schema is as listed as below:

Field Type
id varchar(50)
name varchar(50)
priority int
date date

Domain Object

When the todo example is run, a corresponding domain object is created. The following source code can be viewed in ToDoEvent.java.

public class TodoEvent {
    private String id;
    private String name;
    private int priority;
    private Date date;

    public TodoEvent(String id, String name, int priority, Date date) {
        this.id = id;
        this.name = name;
        this.priority = priority;
        this.date = date;
    }

    // getter and setter methods are omitted, please refer to the source code.
}

Data Access Object

In order to easily access the data in our database, a DAO object with the methods of findAll(),delete(),insert() and update() is needed. The following source code can be seen in EventDAO.java when the ToDo example is run.

public class EventDAO {
    // The implementation is omitted, please refer to the source code.
	public List<TodoEvent> findAll() {
	}
	
	public boolean delete(TodoEvent evt) {
	}
	
	public boolean insert(TodoEvent evt) {
	}
	
	public boolean update(TodoEvent evt) {
    }
}

View

Your first ZK component

The first step is to create a file which the file extension must be zul, such as todo.zul, and place this file under the home directory of your web application, for example, $TOMCAT_HOME/webapps/ProjectName/. The way you can declare a ZK component is pretty much the same as you declare a component using HTML.

Try declaring your first window component as follows:

<window title="To do list" border="normal">
</window>

Then start Tomcat and use a browser to visit this page, eg. http://localhost:8080/todo/todo.zul. The result is shown above, a window with the title of To do List.


Everything in ZK is a component. As such, you can change the title, width, and border of your window as you want. It is quite simple and intuitive. Try to change these attributes, and see the result.

Hierarchical relationships among ZK components

Next, let's try to enrich this page by adding more ZK components. Since we need to show data in a table, we could utilize a listbox component for data display. To insert a listbox, we have to declare it within the enclosing tags of the window component, as follows:

 <window title="To do list" border="normal">
  <listbox id="box" multiple="true" rows="5">
  </listbox>
 </window>

In this example, the listbox component is a child component of the window. Yes, there are hierarchical relationships among ZK components, and you will encounter a UI exception if you try to declare a component within a wrong context, for example, declaring a window component as a child of a listbox component.

A nested component

A listbox is a nested component, which supports two different kinds of child components, listhead (aka: column of table), and listitem (aka: row of table). Within the declaration of the listbox, we set its id attribute to box. We can now use this attribute for listbox reference.

<window id="win" title="To do list" width="640px" border="normal">
	<listbox id="box" multiple="true" rows="5">
		<listhead>
		</listhead>		
		<listitem>	
		</listitem>
	</listbox>
</window>

However, this is not finished yet. Let's declare three listheader components within the enclosing tags of listhead as we require three columns -- Item, Priority, and Date -- within the table. Please see the following:

<window id="win" title="To do list" width="640px" border="normal">
	<listbox id="box" multiple="true" rows="5">
		<listhead>
			<listheader label="Item" />
			<listheader label="Priority" width="80px" />
			<listheader label="Date" width="170px" />
		</listhead>		
		<listitem>
		</listitem>
	</listbox>
</window>


Since we have three columns in our table and each table row also requires three fields, we have to declare three listcell components within the enclosing tags of the listitem component.

<window id="win" title="To do list" width="640px" border="normal">
	<listbox id="box" multiple="true" rows="5">
		<listhead>
			<listheader label="Item" />
			<listheader label="Priority" width="80px" />
			<listheader label="Date" width="170px" />
		</listhead>		
		<listitem>			
			<listcell />
			<listcell />
			<listcell />
		</listitem>
	</listbox>
</window>

The nested structure of the listbox component is as follows:

listbox 
+-- listhead
|    |
|    +-- listheader
|
+-- listitem
      |
     +-- listcell

Input components

In addition to displaying events in the listbox, we need to input information about the event, including the event name (text value), event priority (numeric value) and the event date (date value). To accomplish that, we declare a textbox, an inbox and a datebox within the enclosing tags of the window component. Please see the following:

<window id="win" title="To do list" width="640px" border="normal">
	<listbox id="box" multiple="true" rows="5">
		<listhead>
			<listheader label="Item" />
			<listheader label="Priority" width="80px" />
			<listheader label="Date" width="170px" />
		</listhead>		
		<listitem>			
			<listcell />
			<listcell />
			<listcell />
		</listitem>
	</listbox>
	Item: <textbox id="name" cols="25" />
	Priority: <intbox id="priority" cols="1" />
	Date: <datebox id="date" cols="8" />
	<button id="add" label="Add" />
	<button id="update" label="Update" />
	<button id="delete" label="Delete" />
</window>

Layout components

To distinguish these input components from the above listbox we need to declare a groupbox to group these components together. This will draw a border around all the components contained within the groupbox. In this case, they are the input components.

<window id="win" title="To do list" width="640px" border="normal">
	<listbox id="box" multiple="true" rows="5">
		<listhead>
			<listheader label="Item" />
			<listheader label="Priority" width="80px" />
			<listheader label="Date" width="170px" />
		</listhead>		
		<listitem>			
			<listcell />
			<listcell />
			<listcell />
		</listitem>
	</listbox>
	<groupbox>
		<caption label="Event" />
		Item: <textbox id="name" cols="25" />
		Priority: <intbox id="priority" cols="1" />
		Date: <datebox id="date" cols="8" />
		<button id="add" label="Add" />
		<button id="update" label="Update" />
		<button id="delete" label="Delete" />
	</groupbox>	
</window>

In addition to the groupbox component, we declare a caption component to show an Event label along the top of the group box. The caption component works in a similar manner to the HTML legend element.

Controller

Our requirements include displaying, adding, editing and deleting events. In the following paragraphs, we will implement the interaction between our web application page and the application database.

Defining a Controller

The first step is to define an EventController which extends org.zkoss.zk.ui.util.GenericForwardComposer.

Implementing org.zkoss.zk.ui.util.GenericForwardComposer

Create an EventController to extend org.zkoss.zk.ui.util.GenericForwardComposer allows you to easily access the View and define Create, Read, Update and Delete (CRUD) related methods in the Java file.

// org.zkforge.todo.event.EventController.java

public class EventController extends GenericForwardComposer {
    private static final long serialVersionUID = -9145887024839938515L;
    private EventDAO eventDao = new EventDAO();

    // Note: Something is omitted at here. You can view detail about this class on source code.

    public List<TodoEvent> getAllEvents() {
        return eventDao.findAll();
    }

    public void onClick$add() {
    }

    public void onClick$update() {
    }

    public void onClick$delete() {
    }
}

Associating the Controller with the View

To add interaction between the View and Controller, we set the apply attribute of the window component to the path of our EventController.

<window id="win" title="To do list" width="640px" border="normal"
 apply="org.zkforge.todo.event.EventController">

......

Displaying data in a View

To display retrieved data from the database in the View requires only three steps:

Activating the DataBinding mechanism

To make use of the Data Binding mechanism, we have to activate the Data Binding Manager by defining a page Initializer(org.zkoss.zkplus.databind.AnnotateDataBinderInit) at the top of every page.

<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit"?>

<window id="win" title="To do list" width="640px" border="normal"
 apply="org.zkforge.todo.event.EventController">
....

Associating the data with View

The next step is to retrieve data from the database via the EventController by using the expression(win$composer.allEvents) to invoke EventController.getAllEvents(). The method EventController.getAllEvents() returns a list of all events stored in the database which can be associated with the model attribute of Listbox.


<window id="win" title="To do list" width="640px" border="normal"
 apply="org.zkforge.todo.event.EventController">
	<listbox id="box" multiple="true" rows="5" model="@{win$composer.allEvents}">
....

You can go to the next step to define a UI template with the aim of rendering the Data into the View.

Defining a Template

It is possible to define a UI template for DataBinding to render data into corresponding UI components. This is achieved by utilizing the self attribute of listitem to define a variable to represent each ToDoEvent instance. Each listcell is then used to display data for each ToDoEvent.

<window id="win" title="To do list" width="640px" border="normal"
 apply="org.zkforge.todo.event.EventController">
	<listbox id="box" multiple="true" rows="5"
     model="@{win$composer.allEvents, load-after='add.onClick, delete.onClick, update.onClick'}" 
     selectedItem="@{win$composer.current}">
		<listhead>
			<listheader label="Item" sort="auto(name)" />
			<listheader label="Priority" width="80px" sort="auto(priority)" />
			<listheader label="Date" width="170px" sort="auto(date)" />
		</listhead>		
		<listitem self="@{each='event'}" value="@{event}">			
			<listcell label="@{event.name}" />
			<listcell label="@{event.priority}" />
			<listcell label="@{event.date}" />
		</listitem>
	</listbox>

For more information, please refer to Associate UI Components with a Collection.

Adding the sorting feature

The sorting feature can be added easily by specifying the sort attribute with an expression describing how to sort the column. For example, we could specify auto(name) for the first listheader since it is going to show the name of the todo event. Similarly, auto(priority) can be used for sorting based on the priority of the todo event.

Synchronizing Views

Once the user selects a ToDoEvent in the listbox, the ToDoEvent will need to be displayed in the groupbox as well. It would be the best way if these tedious jobs are done automatically. Data Binding is the best solution to this riddle! With DataBinding, you can simply associate data with all the related UI components. DataBinding will synchronize the state of all UI components once the property of data is modified.

Define a "ToDoEvent" Instance within a Controller

Define a ToDoEvent instance in EventController and define getter and setter methods so that it can be accessed by the View.

// org.zkforge.todo.event.EventController.java

public class EventController extends GenericForwardComposer {
    private TodoEvent current = new TodoEvent();
    // Omitted...
    public TodoEvent getCurrent() {
        return current;
    }

    public void setCurrent(TodoEvent current) {
        this.current = current;
    }

Associating Multiple UI components with the EventController instance

Firstly, you associate the EventController instance with the selectedItem property of the listbox. Then, you associate the properties of ToDoEvent with the corresponding UI components, including textbox, intbox, and datebox. Once the user selects an item in listbox, the instance will be updated accordingly along with the UI components.

	<listbox id="box" multiple="true" rows="5"
      model="@{win$composer.allEvents, load-after='add.onClick, delete.onClick, update.onClick'}" 
      selectedItem="@{win$composer.current}">
	<!-- Omitted -->
	<groupbox>
		<caption label="Event" />
		Item: <textbox id="name" cols="25" value="@{win$composer.current.name}" />
		Priority: <intbox id="priority" cols="1" value="@{win$composer.current.priority}" />
		Date: <datebox id="date" cols="8" value="@{win$composer.current.date}" />

Synchronizing the View and Database

In addition to displaying data from the database in the View, we would like to implement add, update and delete events. This functionality requires three steps: monitoring user's activity; interacting with the Controller to update database; and updating the View. They can be done by the following approaches.

Registering Event Listener in Controller

To monitor user's activities, you simply register an onClick event listener with the following three buttons: add; update and delete. When these buttons are clicked, corresponding methods defined in EventController will be invoked. These methods are defined below:

public class EventController extends GenericForwardComposer {
    private EventDAO eventDao = new EventDAO();
    // Omitted...
    public void onClick$add() {
        if (current != null) {
            current.setId(UUID.randomUUID().toString());
            
            if (validate(current)) {
                // insert into database
                eventDao.insert(current);
            }
        }
    }

    public void onClick$update() {
        if (current != null && validate(current)) {
            // update database
            eventDao.update(current);
        }
    }

    public void onClick$delete() {
        if (current != null && validate(current)) {
            eventDao.delete(current);
        }
    }
}

Set the button id attributes correspondingly to its respective controller method.

		<button id="add" label="Add" />
		<button id="update" label="Update" />
		<button id="delete" label="Delete" />

See the javadoc for more clarification.

Update the View using Databinding

After interacting with the database, the last step is to update the View. You can simply notify Databinding to update the model for you after the user clicks any of the following three buttons: add; update or delete. Try to register load-after attribute with the model attribute of listbox, and then Databinding will update the view automatically if a specified event is triggered.

<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit"?>

<window id="win" title="To do list" width="640px" border="normal"
 apply="org.zkforge.todo.event.EventController">
	<listbox id="box" multiple="true" rows="5"
     model="@{win$composer.allEvents, load-after='add.onClick, delete.onClick, update.onClick'}" 
     selectedItem="@{win$composer.current}">
	<!-- Omitted... -->
		<button id="add" label="Add" />
		<button id="update" label="Update" />
		<button id="delete" label="Delete" />
	</groupbox>	
</window>

Last Update : 2011/12/06