Creating a Database-driven Application
Before You Start
Translations are available in English, Français, Español, Italiano and 日本語.
Other Introductions
- Tutorial - A brief tutorial guides you through the most fundamental features and concepts of ZK.
- Creating Hello World with Eclipse and ZK Studio - Step by step to create a Web application from scratch with Eclipse and ZK Studio.
- Creating a simple sightseeing application - A tutorial exploring some key elements of ZK by creating a sightseeing application without using database.
- ZK Essentials - A book which walks you through the key concepts and features by building a working application from the ground up.
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 require that users 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, 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 online.
Download the ZK Todo file
- Download zk-todo-0.9.zip.
- Unzip zk-todo-0.9.zip, which contains the following todo.war, hsqldb folder, and a readme.txt.
Running the sample application without an IDE
- Copy the hsqldb folder to the root directory where you installed the Apache Tomcat. (ex.C:\)
- Copy todo.war to the $TOMCAT_HOME\webapps directory.
- Start Apache Tomcat.
- Open your browser and navigate to http://localhost:8080/todo.
Running the sample application with an IDE
- Copy hsqldb folder to the root directory where your eclipse workspace located. (ex.C:\).
- Activate Eclipse.
- Select File > Import .
- In the Import dialog, select Web > WAR file and then click Next.
- Using the Browse button to locate todo.war.
- Click Finish to import the Web Project.
- Right click on todo project in the explorer and select Run As > Run on Server
- Select Apache > Tomcat v6.0 Server in the server type dialog and click Finish
- A browser will be activated automatically to explore the todo example.
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 as listed 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 ommited, 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 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, 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 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 below, 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 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 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 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 done yet. Let's declare three listheader components within the enclosing tags of listhead since 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, 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. 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 declare a groupbox to group the 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 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 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 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 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. Data Binding is the answer! You simply associate the data with all 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, 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>