Creating a Database-driven Application"

From Documentation
Line 463: Line 463:
  
 
----
 
----
Last Update : {{REVISIONYEAR}}/{{REVISIONMONTH}}/{{REVISIONDAY}}
+
{{LastUpdated}}

Revision as of 08:07, 17 December 2010

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 does requires that the user have at least a small amount of experience with Java. Do not worry if you lack 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, you might 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 do 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 on line.

Download the ZK Todo file

  1. Download zk-todo-0.9.zip.
  2. Unzip zk-todo-0.9.zip, the zip 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 will need the following attributes: event id, event name, event priority and event date.

The database schema is listed below.

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

Domain Object

For the above table, we create a corresponding domain object, as follows:

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 ommited, please refer to the source code.
}

Data Access Object

In order to easily access the data in our database we need a DAO object with the following methods: findAll(),delete(),insert() and update().

public class EventDAO {
    // The implementation is ommited, 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 whose file extension must be zul, say todo.zul, and place this file under the home directory of your web application, for example,$TOMCAT_HOME/webapps/ProjectName/. You declare a ZK component in much the same way 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 below, a window with the title 「To do List」.


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

Hierarchical relationships among ZK components

Next, let us try to enrich this page with more ZK components. Since we need to show data in a table, we could utilize a listbox component which is designed for displaying data. To insert a listbox we 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 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 to reference the listbox.

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

We are not yet finished. Let』s declare three listheader components within the enclosing tags of listhead since we require three columns -- 「Item」, 「Priority」, and 「Date」 -- within the table, as follows:

<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, each table row also requires three fields. 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 these 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 intbox and a datebox within the enclosing tags of the window component, as follows:

<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 declare a groupbox to group the components together. This will draw a border around all the components contained within the groupbox, in this case, 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's 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 which extends org.zkoss.zk.ui.util.GenericForwardComposer allowing you to easily access the View and define 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 implement the 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 only requires three steps:

Activating the DataBinding mechanism

To make use of the DataBinding mechanism, we have to activate the Data Binding Manager by defining a page Initializer(org.zkoss.zkplus.databind.AnnotateDataBinderInit) at the top of the 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 database via the EventController by using the expression(win$composer.allEvents) to invoke EventController.getAllEvents(). The method EventController.getAllEvents() returns a list that contains all events stored in the database which can be associated to 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 follow the next step to define a UI template to render the Data into 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 the data of 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.

Add 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 <ocde>auto(name) for the first listheader since it is going to show the name of the todo event. Similarly, auto(priority) is used to sort based on the priority of the todo event.

Synchronizing Views

Once the user selects a ToDoEvent in the listbox, we need to display it in the groupbox too. It would be best if these tedious jobs are done automatically. DataBinding is the answer! You simply associate the data with all related UI components. DataBinding will then synchronize the state of all UI components once the property of data has been 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, associate the EventController instance with the selectedItem property of the listbox. Then, 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.

Register Event Listener in Controller

To monitor user's activities, simply register an onClick event listener on the following three buttons, add, update and delete. When clicking these buttons 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 to correspond to their 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 mile is to update the View. Simply notify Databinding to update the model for you when the user clicks any of the following three buttons. Try to register load-after attribute on model attribute of listbox, and Databinding will update the view automatically if the 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 : 2010/12/17