Friday, July 8, 2011

Struts2, Tiles2, Spring, Hibernate, JQuery - DisplayTag Plugin

Struts2, Tiles2, Spring, Hibernate, JQuery - DisplayTag Plugin

Disclaimer:
All the software applications that appear below represent trademarks and they are the property of their respective owners.
Use and implement my notes in this article at your own risk.

Discussion here:
1. General Considerations;
2. Prerequisites;
3. DisplayTag Plugin;
4. Linking DisplayTags;
5. Struts 2 Actions;
6. DAOs;
7. Annotated Hibernate entities;

1. General Considerations:
So, I decided to put another sample based on the same basis as the previous 5 ones (see the March 2011 blog archive).
This sample is purposed to show how to link 2 DisplayTag plugins together; of course, you may link as many you want if you have a link parameter; the 2 DisplayTags will be populated with records from the tables "zone" and "country" of the "blog" database; you may link for example a 3rd DisplayTag representing table "city" by using cod_country parameter, but this is done in the same manner as linking the countries DisplayTag by the zones one.
The sample will be presented somehow in a "top-down" fashion, ie. starting from View, then Controller, then Model components of the MVC web app architecture.


NOTE: if you don't know for example what is "blog" database, or other aspects, then you should read the "Struts2, Tiles2, Spring, Hibernate, JQuery plugin - General Considerations", which is used as a basis for this sample.

2. Prerequisites:
Make sure you have read the "Struts2, Tiles2, Spring, Hibernate, JQuery plugin - General Considerations", and installed and configured all that is written there;

3. DisplayTag Plugin:
The DisplayTag plugin is represented by displaytag-1.1.1.jar, so you have to install it on your local app path (I had at disposal an older version, available for Struts1, you may check newer versions).
In order to use this plugin, you will have to include the following line in your JSP file:
<<
<%@ taglib uri="http://displaytag.sf.net" prefix="display" %>
>>
Also, if you want to use it combined with jQuery plugin, you will have to include this:
<<
<%@ taglib prefix="sj" uri="/struts-jquery-tags"%>
>>
Thus, you made the DisplayTag and jQuery plugins available to be used in your JSP file.

A DisplayTag used for zones will look like:
<<
            <display:table name="displayZoneList"
                id="row"
                requestURI="displayZone.action"
                style="border-width:0;background-color:#a1a5a9; width: 100%; "
                cellspacing="1" cellpadding="0" class="simpletable"
                decorator="blog.decorators.SimpleDecorator"
                export="false"
                pagesize="10">
                <display:column sortable="true"
                    class="tableCell"
                    title="Nr."
                    style="text-align: center;">
                    <s:property value="#attr.row_rowNum" />
                </display:column>
                <display:column sortable="true"
                    class="tableCell"
                    title="Code"
                    style="text-align: center;">
                        <s:property value="#attr.row.cod" />
                </display:column>
                <display:column sortable="true"
                    class="tableCell"
                    title="Zone"
                    style="text-align: center;">
                        <s:property value="#attr.row.name" />
                </display:column>
                <display:column sortable="false"
                    class="tableCell"
                    title="Countries"
                    style="text-align: center;"       
                    media="html"
                    onclick="this.parentNode.className = 'row_selected'">
                    <sj:a id="aa_%{#attr.row.cod}" href="#">countries</sj:a>
                </display:column>
                <display:setProperty name="paging.banner.include_first_last" value="true" />
                <display:setProperty name="paging.banner.placement" value="bottom" />
            </display:table>
>>
Some things here:
- the rendered ID attribute for the HTML object will be "row"; all the date will be then accessed by using "#attr.row".
- the "name" attribute ("displayZoneList") of DisplayTag will represent the Java list used as data source for DisplayTag;
- the "requestURI" attr tells DisplayTag the action that contains the data source list;
- the "decorator" attr is used to CSS stylize the table rows of DisplayTag; it is given by the SimpleDecorator Java class (will put the class code later);
- the DisplayTag has 4 columns: Nr (current record number), Code (the Zone code), Zone (the Zone name), Countries (link to the Countries of a Zone);
- properties regarding table pagination.

The SimpleDecorator class:
<<
package blog.decorators;

public class SimpleDecorator extends org.displaytag.decorator.TableDecorator {
    public String addRowClass() {
        return "dt_" + String.valueOf(getListIndex() % 2);
    }
}
>>
It extends the "org.displaytag.decorator.TableDecorator" and rewrites the "addRowClass" method, returning a string that will be find in the associated CSS file, which will contain:
<<
...
.tableCell {
    color:#00366C;
    background-color: #f2f8ff;
    font-weight: normal;
    font-family: verdana;
    font-size: 10px;
}

tr.dt_0 td.tableCell {
    color: #00366C;
    background-color: #F2F8FF;
    font-weight: normal;
    font-family: verdana;
    font-size: 10px;
}

tr.dt_1 td.tableCell {
    color: #00366C;
    background-color: #D2E8FF;
    font-weight: normal;
    font-family: verdana;
    font-size: 10px;
}
...
>>

4. Linking DisplayTags
Linking the Zones and Countries makes use of the "cod_zone" parameter.
The DisplayTag for countries looks like:
<<
            <display:table name="displayCountryList"
                id="rowCountry"
                requestURI="displayCountry.action"
                style="border-width:0;background-color:#a1a5a9; width: 100%; "
                cellspacing="1" cellpadding="0" class="simpletable"
                decorator="blog.decorators.SimpleDecorator"
                export="false"
                pagesize="10">
                <display:column sortable="true"
                    class="tableCell"
                    title="Nr."
                    style="text-align: center;">
                    <s:property value="#attr.rowCountry_rowNum" />
                </display:column>
                <display:column sortable="true"
                    class="tableCell"
                    title="Code"
                    style="text-align: center;">
                        <s:property value="#attr.rowCountry.cod" />
                </display:column>
                <display:column sortable="true"
                    class="tableCell"
                    title="Country"
                    style="text-align: center;">
                        <s:property value="#attr.rowCountry.name" />
                </display:column>
                <display:setProperty name="paging.banner.include_first_last" value="true" />
                <display:setProperty name="paging.banner.placement" value="bottom" />
            </display:table>
>>
Note: The ID (rowCountry) is different than the zones one (row).
Then, the link is done by jQuery on the "countries" link (sj:a tag) of Zones DisplayTag:
<<
$(function() {
    $("a[id^='aa_']").each(function() {
        $(this).unbind("click");
        $(this).click(function(){
            $.ajax({
                url: "displayCountry.action?code_zone=" + $(this).attr("id").substring("aa_".length),
                type: "POST",
                async: false,
                dataType: "html",
                success: function(data){
                    $("#divCountries").html(data);
                  }
            });
        });
    })
});
>>

5. Struts 2 Actions:
A JQuery autocompleter is rendered by various type of sources: Java list, JSON, JavaScript array, aso.
If you want to use JQuery Ajax capabilities, then you should render a JQuery autocompleter by using JSON type source.
When using Struts, the results are rendered by using Struts Actions, which represent the level of Controller from MVC architecture.
So, the Struts action should return a JSON type result.
To be sure of this, you have to include this annotation to your struts action:
<<
@ParentPackage("blog-struts")
>>
This will use the Struts "blog-struts" package defined in struts.xml, ie. it will extend the "struts-default" package of the Struts2; the "struts-default" package may also render tiles type results.

To populate for example the zones, you will use this code:
<<
    @Override
    @Action(value="/index", results={
        @Result(name="success", location="page.displayzone", type="tiles"),
        @Result(name="input", location="page.empty", type="tiles")
    })
    public String execute() {
        try {
            setDisplayZoneList((List<Zone>)zoneDAO.findAll());
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return ActionSupport.INPUT;
        }
        return ActionSupport.SUCCESS;
    }
>>

Here:
"displayZoneList" is the Java List which will be rendered by the DisplayTag;
"zoneDAO" is the DAO (see "6. DAOs") managed by Spring over the Hibernate entities which contains methods as "findAll()" in order to obtain zones data from database;

Also, you may observe here annotations to define the "success" and "input" results (overcomes) of the action.

In the same action you should define getters and setters for "List<Zone> displayZoneList".

Same you should do for countries and cities.
NOTE: to see how the Struts 2 Actions are accessed, and how their results are rendered, checkout also the "struts.xml" and "tiles.xml" files.

6. DAOs
DAOs, namely Data Access Objects, are used by Spring to obtain data from database via Hibernate.
DAOS are part of the business layer of a database web app.
A DAO class extends in this case the "HibernateDaoSupport" and contains methods to access the database via annotated Hibernate entities.
Any DAO should be described as a bean in the "applicationContext.xml" file in order to be seen by the sessionFactory; for example, for zones:
<<
    <bean id="ZoneDAO" class="blog.hibernate.dao.ZoneDAO">
        <property name="sessionFactory">
            <ref bean="sessionFactory" />
        </property>
    </bean>
>>

From Java point of view, a DAO should use a session manager, in this case, "HibernateDaoSupport" which is a Spring class that manages Hibernate sessions over annotated entities.

NOTE: a method that uses Hibernate session to get data from database uses HQL (Hibernate Query Language); to understand the HQL syntax you should google for HQL documentation.

7. Annotated Hibernate entities
An annotated entity is a POJO class that describes a database table by means of annotations.
It represents the Model from the MVC architecture.
For example, for the database table "zone", it was defined the Zone.java entity class; to refer table "zone" from database "blog", annotations are placed at the top of the class definition:
<<
@Entity
@Table(name = "zone", catalog = "blog")
>>
, while for each of table "zone" fields, there are specified on their value getters: primary key, foreign key, column name, field length, aso, if they exist.
Here are some examples of annotations, taken from Zone.java:
<<
    @Id
    @GeneratedValue
    @Column(name = "cod", unique = true, nullable = false, length = 4)
>>
<<
    @Column(name = "name", nullable = false, length = 45)
>>
<<
    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "zone")
>>

NOTE: if MyEclipse is used as IDE, you may generate both DAO and entity for each table by using Hibernate Reverse Engineering perspective.

No comments:

Post a Comment