MVVM CRUD with Spring 3.1 and Hibernate 4 and ZK - Part 2

This article focus on Adding new address and refresh the Listing screen

In the first article, we have seen how to configure spring and hibernate and also we have written generic service and dao layer.  In this article , we will see how to add new address using CRUD screen.


Step 1:

Let us add the New address button and on click event in the View Modal . Let us modify the Addresslist.zul as shown here.
<?page title="Address List" contentType="text/html;charset=UTF-8"?>
<zk>
<window id="addressList" title="Address List" border="none"
apply="org.zkoss.bind.BindComposer"
viewModel="@id('vm') @init('zkoss.vm.AddressListVM')">
<separator />
<button label="New Address" onClick="@command('onAddNew')"></button>
<separator />
<listbox id="" mold="paging" pageSize="11" pagingPosition="top"
selectedItem="@bind(vm.selectedItem)" model="@load(vm.dataSet)">
<listhead sizable="true">
<listheader label="Full Name" sortDirection="ascending"
sort="auto(fullName)" />
<listheader label="Email" sort="auto(email)" />
<listheader label="Mobile" sort="auto(mobile)" />
</listhead>
<template name="model" var="p1">
<listitem>
<listcell label="@load(p1.fullName)" />
<listcell label="@load(p1.email)" />
<listcell label="@load(p1.mobile)" />
</listitem>
</template>
</listbox>
</window>
</zk>

We have used command binding mechanism to do the action when the user press the button. To learn more on Command binding in zk, check here.
 
Step 2:

Next we will update our view model to handle on click event of the button. We will update our AddressListVM as follows;
package zkoss.vm;

import java.util.HashMap;
import java.util.List;

import org.zkoss.bind.annotation.AfterCompose;
import org.zkoss.bind.annotation.BindingParam;
import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.ContextParam;
import org.zkoss.bind.annotation.ContextType;
import org.zkoss.bind.annotation.GlobalCommand;
import org.zkoss.bind.annotation.NotifyChange;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.select.Selectors;
import org.zkoss.zk.ui.select.annotation.WireVariable;
import org.zkoss.zkplus.spring.SpringUtil;

import AddressBook.domain.AddressBook;
import AddressBook.service.CRUDService;

public class AddressListVM {

@WireVariable
private CRUDService CRUDService;

private AddressBook selectedItem;
private List<AddressBook> allReordsInDB = null;

public AddressBook getSelectedItem() {
return selectedItem;
}

public void setSelectedItem(AddressBook selectedItem) {
this.selectedItem = selectedItem;
}

@AfterCompose
public void initSetup(@ContextParam(ContextType.VIEW) Component view) {
Selectors.wireComponents(view, this, false);
CRUDService = (CRUDService) SpringUtil.getBean("CRUDService");
allReordsInDB = CRUDService.getAll(AddressBook.class);
}

public List<AddressBook> getDataSet() {
return allReordsInDB;
}

@Command
public void onAddNew() {
final HashMap<String, Object> map = new HashMap<String, Object>();
map.put("selectedRecord", null);
map.put("recordMode", "NEW");
Executions.createComponents("AddressCRUD.zul", null, map);
}

// note this will be executed from AddressListCRUDVM.java

@GlobalCommand
@NotifyChange("dataSet")
public void refreshList(@BindingParam("selectedRecord") AddressBook addressbook,
@BindingParam("recordMode") String recordMode) {
if (recordMode.equals("NEW")) {
allReordsInDB.add(addressbook);
}
}

}

You can see that, we have written a method called onAddNew and annotated with @Command. Inside this method, we will call another ZUL File (AddressCRUD.ZUL) in which we will add new address and edit the existing address.

And also, below the onAddNew method, we have another method called refreshList and annotated with @GlobalCommand. This method will be called from the child window (AddressCRUD.Zul) to refresh the parent (addresslist.zul) after new record is added.
For more information about ZK Global Command, look here.


Step 3:

Next we will add new ZUL File AddressCRUD.zul to add and edit the existing the address in the database.

image

Here is the code.


<?page title="new page title" contentType="text/html;charset=UTF-8"?>
<zk>
<window title="Address" id="addressCRUD" border="normal" width="30%"
mode="modal" maximizable="false" closable="true"
position="center,parent" apply="org.zkoss.bind.BindComposer"
viewModel="@id('vm') @init('zkoss.vm.AddressListCRUDVM')">
<panel width="100%">
<panelchildren>
<separator />
<grid>
<columns>
<column></column>
<column></column>
</columns>
<rows>
<row>
<cell colspan="2">
<vlayout>
<label value="Full Name" />
<textbox id="fullName" hflex="1"
value="@load(vm.selectedRecord.fullName) @save(vm.selectedRecord.fullName, before='saveThis')"
mold="rounded" />
</vlayout>
</cell>
</row>
<row>
<cell colspan="2">
<vlayout>
<label value="Address" />
<textbox id="address1" hflex="1"
value="@load(vm.selectedRecord.address1) @save(vm.selectedRecord.address2, before='saveThis')"
mold="rounded" />
</vlayout>
</cell>
</row>
<row>
<cell colspan="2">
<vlayout>
<textbox id="address2" hflex="1"
value="@load(vm.selectedRecord.address2) @save(vm.selectedRecord.address2, before='saveThis')"
mold="rounded" />
</vlayout>
</cell>
</row>
<row>
<vlayout>
<label value="City" />
<textbox id="City" hflex="1"
value="@load(vm.selectedRecord.city) @save(vm.selectedRecord.city, before='saveThis')"
mold="rounded" />
</vlayout>
<vlayout>
<grid sclass="vgrid">
<columns>
<column width="30%"></column>
<column></column>
</columns>
<rows>
<row>
<vlayout>
<label value="State" />
<textbox id="State"
value="@load(vm.selectedRecord.state) @save(vm.selectedRecord.state, before='saveThis')"
hflex="1" mold="rounded" />
</vlayout>
<vlayout>
<label value="ZipCode" />
<textbox id="zipcode"
value="@load(vm.selectedRecord.zipCode) @save(vm.selectedRecord.zipCode, before='saveThis')"
hflex="1" mold="rounded" />
</vlayout>
</row>
</rows>
</grid>
</vlayout>
</row>
<row>
<cell colspan="2">
<vlayout>
<label value="Email" />
<textbox id="email" hflex="1"
value="@load(vm.selectedRecord.email) @save(vm.selectedRecord.email, before='saveThis')"
mold="rounded" />
</vlayout>
</cell>
</row>
<row>
<vlayout>
<label value="Mobile" />
<textbox id="Mobile" hflex="1"
value="@load(vm.selectedRecord.mobile) @save(vm.selectedRecord.mobile, before='saveThis')"
mold="rounded" />
</vlayout>
</row>

</rows>
</grid>
</panelchildren>
</panel>
<separator />
<separator />
<separator />
<div align="center">
<button label="Save" onClick="@command('saveThis')"
mold="trendy" />
<button label="Cancel" onClick="@command('closeThis')"
mold="trendy" />
</div>
<separator />
</window>
</zk>

Here. we are using ZK MVVM Data binding concept to load and save the values in the ZK Components. For more information about ZK Data binding , please look here and here.


Step 4:

Next we will add View modal for the above zul file as follows

package zkoss.vm;

import java.util.HashMap;
import java.util.Map;

import org.zkoss.bind.BindUtils;
import org.zkoss.bind.annotation.AfterCompose;
import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.ContextParam;
import org.zkoss.bind.annotation.ContextType;
import org.zkoss.bind.annotation.ExecutionArgParam;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.select.Selectors;
import org.zkoss.zk.ui.select.annotation.Wire;
import org.zkoss.zk.ui.select.annotation.WireVariable;
import org.zkoss.zkplus.spring.SpringUtil;
import org.zkoss.zul.Window;

import AddressBook.domain.AddressBook;
import AddressBook.service.CRUDService;

public class AddressListCRUDVM {

@Wire("#addressCRUD")
private Window win;

@WireVariable
private CRUDService CRUDService;

private AddressBook selectedRecord;
private String recordMode;

public AddressBook getSelectedRecord() {
return selectedRecord;
}

public void setSelectedRecord(AddressBook selectedRecord) {
this.selectedRecord = selectedRecord;
}

public String getRecordMode() {
return recordMode;
}

public void setRecordMode(String recordMode) {
this.recordMode = recordMode;
}

@AfterCompose
public void initSetup(@ContextParam(ContextType.VIEW) Component view,
@ExecutionArgParam("selectedRecord") AddressBook addressBook,
@ExecutionArgParam("recordMode") String recordMode)
throws CloneNotSupportedException {
Selectors.wireComponents(view, this, false);
setRecordMode(recordMode);
CRUDService = (CRUDService) SpringUtil.getBean("CRUDService");

if (recordMode.equals("NEW")) {
this.selectedRecord = new AddressBook();
}

}

@Command
public void saveThis() {
Map<String, Object> args = new HashMap<String, Object>();
CRUDService.Save(this.selectedRecord);
args.put("selectedRecord", this.selectedRecord);
args.put("recordMode", this.recordMode);
BindUtils.postGlobalCommand(null, null, "refreshList", args);
win.detach();
}

@Command
public void closeThis() {
win.detach();
}
}

Step 5:

Next we need to add save method in both service and dao layers as follows.
CRUDService.java

package AddressBook.service;

import java.util.List;

public interface CRUDService {
<T> List<T> getAll(Class<T> klass);
<T> void Save(T klass);
}


CRUDServiceImpl.java

package AddressBook.service;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import AddressBook.dao.CRUDDao;

@Service
public class CRUDServiceImpl implements CRUDService {

@Autowired
private CRUDDao CRUDDao;

@Transactional(readOnly = true)
public <T> List<T> getAll(Class<T> klass) {
return CRUDDao.getAll(klass);
}

@Transactional
public <T> void Save(T klass) {
CRUDDao.Save(klass);
}


}

CRUDDao.java

package AddressBook.dao;

import java.util.List;

public interface CRUDDao {
<T> List<T> getAll(Class<T> klass);
<T> void Save(T klass);


}

CRUDDaoImpl.java

package AddressBook.dao;

import java.util.List;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

@Repository
public class CRUDDaoImpl implements CRUDDao {

@Autowired
SessionFactory sessionFactory;

@SuppressWarnings("unchecked")
public <T> List<T> getAll(Class<T> klass) {
return getCurrentSession().createQuery("from " + klass.getName())
.list();
}

protected final Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}

public <T> void Save(T klass) {
getCurrentSession().save(klass);
}

}

Step 6:

Now we can run addresslist.zul and click on the button to add new address.

You can download the source here

Project Structure.
image