Tuesday, 7 August 2012

ZK Hibernate one to Many annotation mapping bidirectional CRUD example using MVVM

In this post, we will see how we can implement hibernate one to Many mapping (master & detail) bidirectional using ZK Components

If you new to hibernate, please look the following URL to understand better on one to many mapping in hibernate.
Example1
Example2

Now let us start with ZK.

Step : 1

Let us set up the environment. Follow this post to set up Hibernate with ZK in eclipse IDE.

 

Step : 2

Let us set up the mysql tables as follows.

image_thumb6_thumb[4]


Now let us start creating individual files

Department.java

package domain;


import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.OneToMany;
@Entity
@Table(name = "department")
public class Department implements Cloneable {

@Id
@GeneratedValue
@Column(name = "ID")
private int ID;

@Column(name = "DepartName")
private String departName;


/**
mappedBy – means “I am not the owner side”, I am mapped by Employee from the other side of the relationship.
It will also not create the database column which makes sense, I would expect a foreign key on the Employee table instead.
Here we have used department as mapped by, so there must be property on the Employee Class.

Department has a bidirectional one to many relationship with Employee through the department property.
You don't have to (must not) define any physical mapping in the mappedBy side.

orphanRemoval=true is used because, even though we specified cascadetype all, but if only child records are deleted,
then when you save the master, the child will not delete. for this, we have used orphanRemoval=true.

**/

@OneToMany(fetch=FetchType.EAGER, cascade = CascadeType.ALL, mappedBy="department", orphanRemoval=true)
List<Employees> employees;

public int getID() {
return ID;
}

public void setID(int iD) {
ID = iD;
}

public String getDepartName() {
return departName;
}

public void setDepartName(String departName) {
this.departName = departName;
}

public List<Employees> getEmployees() {
return employees;
}

public void setEmployees(List<Employees> employees) {
this.employees = employees;
}

public Object clone() throws CloneNotSupportedException {
return super.clone();
}


}


Employees.java


package domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.ManyToOne;
import javax.persistence.JoinColumn;


@Entity
@Table(name = "employees")
public class Employees implements Cloneable {

@Id
@GeneratedValue
private int ID;
@Column(name = "firstName")
private String firstName;

@Column(name = "lastName")
private String lastName;

@ManyToOne
@JoinColumn(name="DepartmentID")
private Department department;

public int getID() {
return ID;
}

public void setID(int iD) {
ID = iD;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public Department getDepartment() {
return department;
}

public void setDepartment(Department department) {
this.department = department;
}

public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}


DepartmentDAO.java


package domainDAO;

import java.util.List;

import domain.Department;

import org.hibernate.Query;
import org.hibernate.Session;

import HibernateUtilities.HibernateUtil;

public class DepartmentDAO {

@SuppressWarnings("unchecked")
public List<Department> getAllDepartment() {
List<Department> allrecords = null;
try {
Session session = HibernateUtil.beginTransaction();
Query q1 = session.createQuery("from Department");
allrecords = q1.list();
HibernateUtil.CommitTransaction();
} catch (RuntimeException e) {
e.printStackTrace();
}
return allrecords;
}

public void saveOrUpdate(Department p1) {

try {
Session session = HibernateUtil.beginTransaction();
session.saveOrUpdate(p1);
HibernateUtil.CommitTransaction();
} catch (RuntimeException e) {
e.printStackTrace();
}

}

public Integer delete(Department p1) {
try {
Session session = HibernateUtil.beginTransaction();
session.delete(p1);
HibernateUtil.CommitTransaction();
return 1;
} catch (RuntimeException e) {
e.printStackTrace();
return 0;
}

}
}


DepartmentCRUDVM.java


package domainVMS;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.zkoss.bind.BindUtils;
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.ExecutionArgParam;
import org.zkoss.bind.annotation.GlobalCommand;
import org.zkoss.bind.annotation.Init;
import org.zkoss.bind.annotation.NotifyChange;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.select.Selectors;
import org.zkoss.zk.ui.select.annotation.Wire;
import org.zkoss.zul.Messagebox;
import org.zkoss.zul.Window;

import bsh.This;

import domain.Department;
import domain.Employees;
import domainDAO.DepartmentDAO;

public class DepartmentCRUDVM {

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

private boolean makeAsReadOnly;
private Department selectedDepartment;
private List<Employees> employees = new ArrayList<Employees>();
private Employees curSelectedEmployee;
private Integer curSelectedEmployeeIndex;
private String recordMode;

public void setEmployees(List<Employees> employees) {
this.employees = employees;
}

public String getRecordMode() {
return recordMode;
}

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

public Integer getCurSelectedEmployeeIndex() {
return curSelectedEmployeeIndex;
}

public void setCurSelectedEmployeeIndex(Integer curSelectedEmployeeIndex) {
this.curSelectedEmployeeIndex = curSelectedEmployeeIndex;
}

public boolean isMakeAsReadOnly() {
return makeAsReadOnly;
}

public void setMakeAsReadOnly(boolean makeAsReadOnly) {
this.makeAsReadOnly = makeAsReadOnly;
}

public Department getSelectedDepartment() {
return selectedDepartment;
}

public void setSelectedDepartment(Department selectedDepartment) {
this.selectedDepartment = selectedDepartment;
}

public List<Employees> getallEmployees() {
return employees;
}

public Employees getCurSelectedEmployee() {
return curSelectedEmployee;
}

public void setCurSelectedEmployee(Employees curSelectedEmployee) {
this.curSelectedEmployee = curSelectedEmployee;
}

@Init
public void initSetup(@ContextParam(ContextType.VIEW) Component view,
@ExecutionArgParam("sDepartment") Department d1,
@ExecutionArgParam("recordMode") String recordMode)
throws CloneNotSupportedException {
Selectors.wireComponents(view, this, false);

setRecordMode(recordMode);
if (recordMode.equals("NEW")) {
this.selectedDepartment = new Department();
}

if (recordMode.equals("EDIT")) {
this.selectedDepartment = (Department) d1.clone();
setEmployees(d1.getEmployees());
}

if (recordMode == "READ") {
setMakeAsReadOnly(true);
win.setTitle(win.getTitle() + " (Readonly)");
}

}

@Command
public void addNewEmployee() {

final HashMap<String, Object> map = new HashMap<String, Object>();
map.put("sEmployee", null);
map.put("recordMode", "NEW");
Executions.createComponents("EmployeeCRUD.zul", null, map);
}

@Command
public void editThisEmployee() {
final HashMap<String, Object> map = new HashMap<String, Object>();
map.put("sEmployee", this.curSelectedEmployee);
setCurSelectedEmployeeIndex(employees.indexOf(curSelectedEmployee));
map.put("recordMode", "EDIT");
Executions.createComponents("EmployeeCRUD.zul", null, map);
}

@SuppressWarnings({ "unchecked", "rawtypes" })
@Command
public void deleteThisEmployee() {

String str = "The \""
+ this.curSelectedEmployee.getLastName()
+ "\" will be permanently deleted and the action cannot be undone.";

Messagebox.show(str, "Confirm", Messagebox.OK | Messagebox.CANCEL,
Messagebox.QUESTION, new EventListener() {
@Override
public void onEvent(Event event) throws Exception {
if (((Integer) event.getData()).intValue() == Messagebox.OK) {
employees.remove(curSelectedEmployee);
BindUtils.postNotifyChange(null, null,
DepartmentCRUDVM.this, "allEmployees");
}
}
});
}

@Command
public void save() {
selectedDepartment.setEmployees(employees);
new DepartmentDAO().saveOrUpdate(selectedDepartment);
Map args = new HashMap();
args.put("pDepartment", this.selectedDepartment);
args.put("recordMode", this.recordMode);
BindUtils.postGlobalCommand(null, null, "updateDepartmentInfo", args);
win.detach();
}

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

@GlobalCommand
@NotifyChange("allEmployees")
public void updateEmployeeInfo(@BindingParam("pEmployee") Employees e1,
@BindingParam("recordMode") String recordMode) {

if (recordMode.equals("EDIT")) {
e1.setDepartment(selectedDepartment);
employees.set(this.curSelectedEmployeeIndex, e1);
}

if (recordMode.equals("NEW")) {
e1.setDepartment(selectedDepartment);
employees.add(e1);
}
}

}


DepartmentListVM.java


package domainVMS;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.zkoss.bind.BindUtils;
import org.zkoss.bind.annotation.BindingParam;
import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.GlobalCommand;
import org.zkoss.bind.annotation.Init;
import org.zkoss.bind.annotation.NotifyChange;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zul.Messagebox;
import domain.Department;
import domainDAO.DepartmentDAO;

public class DepartmentListVM {

private List<Department> departments= new ArrayList<Department>();
private Department curSelectedDepartment;
private Integer curSelectedDepartmentIndex;


public Integer getCurSelectedDepartmentIndex() {
return curSelectedDepartmentIndex;
}
public void setCurSelectedDepartmentIndex(Integer curSelectedDepartmentIndex) {
this.curSelectedDepartmentIndex = curSelectedDepartmentIndex;
}
public Department getCurSelectedDepartment() {
return curSelectedDepartment;
}
public void setCurSelectedDepartment(Department curSelectedDepartment) {
this.curSelectedDepartment = curSelectedDepartment;
}

public List<Department> getallDepartments() {
return departments;
}

@Init
public void initSetup() {
departments= new DepartmentDAO().getAllDepartment();
}

@Command
public void addNewDepartment() {
final HashMap<String, Object> map = new HashMap<String, Object>();
map.put("sDepartment", null);
map.put("recordMode", "NEW");
Executions.createComponents("DepartmentCRUD.ZUL", null, map);
}

@Command
public void editThisDepartment()
{
final HashMap<String, Object> map = new HashMap<String, Object>();
map.put("sDepartment", this.curSelectedDepartment);
map.put("recordMode", "EDIT");
setCurSelectedDepartmentIndex(departments.indexOf(curSelectedDepartment));
Executions.createComponents("DepartmentCRUD.ZUL", null, map);
}

//The following method will be called from DepartmentCRUDVM.java after the save
@GlobalCommand
@NotifyChange("allDepartments")
public void updateDepartmentInfo(@BindingParam("pDepartment") Department d1,
@BindingParam("recordMode") String recordMode) {

if (recordMode.equals("EDIT")) {
departments.set(this.curSelectedDepartmentIndex, d1);
}

if (recordMode.equals("NEW")) {
departments.add(d1);
}
}


@SuppressWarnings({ "unchecked", "rawtypes" })
@Command
public void deleteThisDepartment()
{
int OkCancel;

String str = "The Selected \"" + curSelectedDepartment.getDepartName()
+ "\" will be deleted.";
OkCancel = Messagebox.show(str, "Confirm", Messagebox.OK
| Messagebox.CANCEL, Messagebox.QUESTION);
if (OkCancel == Messagebox.CANCEL) {
return;
}

str = "The \""
+ curSelectedDepartment.getDepartName()
+ "\" will be permanently deleted and the action cannot be undone.";

Messagebox.show(str, "Confirm", Messagebox.OK | Messagebox.CANCEL,
Messagebox.QUESTION, new EventListener() {
@Override
public void onEvent(Event event) throws Exception {
if (((Integer) event.getData()).intValue() == Messagebox.OK) {
new DepartmentDAO().delete(curSelectedDepartment);
departments.remove(departments.indexOf(curSelectedDepartment));
BindUtils.postNotifyChange(null, null,
DepartmentListVM.this, "allDepartments");
}
}
});
}

}


EmployeeCRUDVM.java


package domainVMS;

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

import org.zkoss.bind.BindUtils;
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.bind.annotation.Init;
import org.zkoss.bind.annotation.NotifyChange;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.select.Selectors;
import org.zkoss.zk.ui.select.annotation.Wire;
import org.zkoss.zul.Messagebox;
import org.zkoss.zul.Window;

import domain.Employees;

public class EmployeeCRUDVM {

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

private Employees selectedEmployee;
private Employees selectedEmployeeOrg;

private boolean makeAsReadOnly;
private String recordMode;


public Employees getSelectedEmployeeOrg() {
return selectedEmployeeOrg;
}

public void setSelectedEmployeeOrg(Employees selectedEmployeeOrg) {
this.selectedEmployeeOrg = selectedEmployeeOrg;
}

public String getRecordMode() {
return recordMode;
}

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

public Employees getSelectedEmployee() {
return selectedEmployee;
}

public void setSelectedEmployee(Employees selectedEmployee) {
this.selectedEmployee = selectedEmployee;
}

public boolean isMakeAsReadOnly() {
return makeAsReadOnly;
}
public void setMakeAsReadOnly(boolean makeAsReadOnly) {
this.makeAsReadOnly = makeAsReadOnly;
}

@Init
@NotifyChange("selectedEmployee")
public void initSetup(@ContextParam(ContextType.VIEW) Component view,
@ExecutionArgParam("sEmployee") Employees selectedEmployee,
@ExecutionArgParam("recordMode") String recordMode) throws CloneNotSupportedException {
Selectors.wireComponents(view, this, false);
setRecordMode(recordMode);


if (recordMode.equals("NEW")) {
this.selectedEmployee = new Employees();
}

if (recordMode.equals("EDIT")) {
System.out.println("Value is " + selectedEmployee.getFirstName());
this.selectedEmployeeOrg = selectedEmployee;
this.selectedEmployee= (Employees) selectedEmployee.clone();
}

if (recordMode == "READ") {
setMakeAsReadOnly(true);
win.setTitle(win.getTitle() + " (Readonly)");
}

}

@Command
public void save() {
Map args = new HashMap();
args.put("pEmployee", this.selectedEmployee);
args.put("recordMode", this.recordMode);
BindUtils.postGlobalCommand(null, null, "updateEmployeeInfo", args);
win.detach();
}

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


package HibernateUtilities;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;

public class HibernateUtil {

private static SessionFactory factory;
private static ServiceRegistry serviceRegistry;

public static Configuration getInitConfiguration() {
Configuration config = new Configuration();
config.configure();
return config;
}

public static Session getSession() {
if (factory == null) {
Configuration config = HibernateUtil.getInitConfiguration();
serviceRegistry = new ServiceRegistryBuilder().applySettings(
config.getProperties()).buildServiceRegistry();
factory = config.buildSessionFactory(serviceRegistry);
}
Session hibernateSession = factory.getCurrentSession();
return hibernateSession;
}

public static Session beginTransaction() {
Session hibernateSession;
hibernateSession = HibernateUtil.getSession();
hibernateSession.beginTransaction();
return hibernateSession;
}

public static void CommitTransaction() {
HibernateUtil.getSession().getTransaction().commit();
}

public static void closeSession() {
HibernateUtil.getSession().close();
}

public static void rollbackTransaction() {
HibernateUtil.getSession().getTransaction().rollback();
}

}



HibernateUtilities.cfg.xml


<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost/sampledb</property>
<property name="connection.username">root</property>
<property name="connection.password">123</property>

<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>

<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>

<!-- Enable Hibernate's automatic session context management -->
<property name="current_session_context_class">thread</property>

<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>

<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>



<!-- Mapping Classes -->
<mapping class="domain.Department" />
<mapping class="domain.Employees" />

</session-factory>
</hibernate-configuration>


DepartmentCRUD.ZUL


<?page title="new page title" contentType="text/html;charset=UTF-8"?>
<zk>
<window title="One to Many Hibernate using MVVM" border="normal"
id="DepartmentCRUD" width="430px" height="520px"
apply="org.zkoss.bind.BindComposer" minimizable="false" mode="modal"
maximizable="false" closable="true"
viewModel="@id('vm') @init('domainVMS.DepartmentCRUDVM')">
<separator />
<label value="Department information" />
<separator />
<panel width="100%">
<panelchildren>
<separator />
<grid width="99.5%">
<columns>
<column label="" width="150px" />
<column label="" />
</columns>
<rows>
<row>
<hbox>
<label value="Department Name" />
<label value="*" />
</hbox>
<textbox name="name"
readonly="@load(vm.makeAsReadOnly)"
value="@bind(vm.selectedDepartment.departName)" cols="20" />
</row>
</rows>
</grid>

<separator />
<separator />
<separator />
<separator />

<label value="Employee information" />
<separator />
<div>
<button label="Add New Employee"
onClick="@command('addNewEmployee')" />
</div>
<separator />
<listbox id="test" model="@load(vm.allEmployees)"
selectedItem="@bind(vm.curSelectedEmployee)">
<listhead sizable="true">
<listheader label="First Name" />
<listheader label="Last Name" />
<listheader label="Action" />
</listhead>
<template name="model" var="p1">
<listitem>
<listcell label="@load(p1.firstName)" />
<listcell label="@load(p1.lastName)" />
<listcell>
<hbox spacing="20px">
<image sclass="fimageDelete"
onClick="@command('deleteThisEmployee')" />
<image sclass="fimageedit"
onClick="@command('editThisEmployee')" />
</hbox>
</listcell>
</listitem>
</template>
</listbox>
</panelchildren>
</panel>
<separator />
<div align="center">
<button id="submit" label="Submit"
onClick="@command('save')" visible="@load(not vm.makeAsReadOnly)" />
<button id="cancel"
label="@load(vm.makeAsReadOnly ?'Ok':'Cancel')"
onClick="@command('closeThis')" />
</div>
</window>
</zk>


DepartmentList.zul


<?page title="DepartmentList" contentType="text/html;charset=UTF-8"?>
<zk>
<style>
.z-listcell.red .z-listcell-cnt, .z-label.red{ color:red; }

/* Start: Action Images- Edit
---------------------------------------------- */

.fimageedit { width: 25px; background-image:
url('./images/icon-edit.png'); background-repeat: no-repeat;
border: 0 none; cursor: pointer; }

/* End: Action Images - Edit
---------------------------------------------- */


/* Start: Action Images- Delete
---------------------------------------------- */

.fimageDelete { width: 25px; background-image:
url('./images/icon-trash-red.png'); background-repeat:
no-repeat; border: 0 none; cursor: pointer; }

/* End: Action Images - Delete
---------------------------------------------- */


/* Start: Action Images- Active
---------------------------------------------- */

.fimageActive { width: 25px; background-image:
url('./images/icon-enable.png'); background-repeat: no-repeat;
border: 0 none; cursor: pointer; }

/* End: Action Images - Active
---------------------------------------------- */

/* Start: Action Images- Inactive' ]

---------------------------------------------- */

.fimageInactive { width: 25px; background-image:
url('./images/icon-disable.png'); background-repeat: no-repeat;
border: 0 none; cursor: pointer; }

/* End: Action Images - InActive
---------------------------------------------- */

.z-listcell.highlightcell .z-listcell-cnt,
.z-label.highlightcell { color:blue; cursor: pointer; }


</style>
<window title="One to Many Hibernate using MVVM" border="normal"
apply="org.zkoss.bind.BindComposer"
viewModel="@id('myvm') @init('domainVMS.DepartmentListVM')">

<div>
<button label="Add New Department"
onClick="@command('addNewDepartment')" />
</div>
<separator />

<listbox id="test" model="@load(myvm.allDepartments)"
selectedItem="@bind(myvm.curSelectedDepartment)">
<listhead sizable="true">
<listheader label="Department Name" width="400px"
sort="auto(departName)" />
<listheader label="Action" />
</listhead>
<template name="model" var="p1">
<listitem>
<listcell label="@load(p1.departName)"
onClick="@command('openAsReadOnly')" sclass="highlightcell" />
<listcell>
<hbox spacing="20px">
<image sclass="fimageDelete" onClick="@command('deleteThisDepartment')" />
<image sclass="fimageedit"
onClick="@command('editThisDepartment')" />
</hbox>
</listcell>
</listitem>
</template>
</listbox>
</window>
</zk>


EmployeeCRUD.zul


<zk>
<window id="win" title="Employee" width="520px" height="220px"
border="normal" minimizable="false" mode="modal" maximizable="false"
closable="true" action="show: slideDown;hide: slideUp"
apply="org.zkoss.bind.BindComposer"
viewModel="@id('vm') @init('domainVMS.EmployeeCRUDVM')">
<separator />
<label value="Employee information" />
<separator />
<panel width="100%">
<panelchildren>
<separator />
<grid width="99.5%">
<columns>
<column label="" width="150px" />
<column label="" />
</columns>
<rows>
<row>
<hbox>
<label value="First Name" />
<label value="*" />
</hbox>
<textbox name="name"
readonly="@load(vm.makeAsReadOnly)"
value="@bind(vm.selectedEmployee.firstName)" cols="50" />
</row>
<row>
<hbox>
<label value="Last Name" />
<label value="*" />
</hbox>
<textbox id="txtlastName"
readonly="@load(vm.makeAsReadOnly)"
value="@bind(vm.selectedEmployee.lastName)" />
</row>
</rows>
</grid>
</panelchildren>
</panel>
<separator />
<div align="center">
<button id="submit" label="Submit"
onClick="@command('save')" visible="@load(not vm.makeAsReadOnly)" />
<button id="cancel"
label="@load(vm.makeAsReadOnly ?'Ok':'Cancel')"
onClick="@command('closeThis')" />
</div>
</window>
</zk>




Project Structure

image


 


You can run the departmentlist.zul file and check the output.


 


image


 


You can download the source code here.


You can also download as individual files here.



  1. Department.java
  2. DepartmentCRUD.ZUL
  3. DepartmentCRUDVM.java
  4. DepartmentDAO.java
  5. DepartmentList.zul
  6. DepartmentListVM.java
  7. EmployeeCRUD.zul
  8. EmployeeCRUDVM.java
  9. Employees.java
  10. HibernateUtil.java
  11. icon-edit.png
  12. icon-enable.png
  13. icon-trash-red.png

14 comments:

  1. Many thanks Nathan for the detailed codes.

    best
    TerryTornado

    ReplyDelete
  2. greate tutorial,thanks!

    Ricardo Johannsen

    ReplyDelete
  3. thank you for sharing this informative blog.. this blog really helpful for everyone.. explanation are clear so easy to understand...

    core java training institute in chennai | core java training topics | core java training in chennai | core java training online

    ReplyDelete
  4. We are a third party technical support service. Avast Customer Support is here to help you out with the whole procedure to Download Avast Antivirus online, We not only fix your Avast Support related issues but will guide with how to get started with your new Avast product once it gets installed successfully. Call on our Toll Free no. 1 855 966 3855
    Gmail Customer service is a third party technical support service for Gmail users when they face any technical issue or error in their Gmail account. Our Gmail Customer Support team solves issues like forgot Gmail account password, Gmail configuration or Sync issues, recover deleted emails and many more. Toll Free number (800) 986-9271
    How you install or reinstall Office 365 or Office 2016 depends on whether your Office product is part of an Office for home or Office for business plan. If you're not sure what you have, see what office com setup products are included in each plan and then follow the steps for your product. The steps below also apply if you're installing a single, stand-alone Office application such as Access 2016 or Visio 2016. Need Help with office setup Enter Product Key? Call 1-800-000-0000 Toll Free
    Norton Tech Support is a third party service provider and not in any way associated with Norton or any of its partner companies. We offer support for Norton products and sell subscription based additional warranty on computer and other peripheral devices. Call our Toll Free number 1 855 966 3855
    Other Services
    Norton Toll Free , Office-Setup , office.com/setup.

    ReplyDelete
  5. I accept there are numerous more pleasurable open doors ahead for people that took a gander at your site.we are providing ReactJs training in Chennai.
    For more details: ReactJs training in Velachery | ReactJs training in chennai

    ReplyDelete
  6. Hello, I read your blog occasionally, and I own a similar one, and I was just wondering if you get a lot of spam remarks? If so how do you stop it, any plugin or anything you can advise? I get so much lately it’s driving me insane, so any assistance is very much appreciated.
    Android Course Training in Chennai | Best Android Training in Chennai
    Datascience Course Training in Chennai | Best Data Science Training in Chennai

    ReplyDelete
  7. Interesting information and attractive.This blog is really rocking... Yes, the post is very interesting and I really like it.I never seen articles like this. I meant it's so knowledgeable, informative, and good looking site. I appreciate your hard work. Good job.
    Kindly visit us @
    Sathya Online Shopping
    Online AC Price | Air Conditioner Online | AC Offers Online | AC Online Shopping
    Inverter AC | Best Inverter AC | Inverter Split AC
    Buy Split AC Online | Best Split AC | Split AC Online
    LED TV Sale | Buy LED TV Online | Smart LED TV | LED TV Price
    Laptop Price | Laptops for Sale | Buy Laptop | Buy Laptop Online
    Full HD TV Price | LED HD TV Price
    Buy Ultra HD TV | Buy Ultra HD TV Online
    Buy Mobile Online | Buy Smartphone Online in India

    ReplyDelete
  8. The article is very interesting and very understood to be read, may be useful for the people. I wanted to thank you for this great read!! I definitely enjoyed every little bit of it. I have to bookmarked to check out new stuff on your post. Thanks for sharing the information keep updating, looking forward for more posts..
    Kindly visit us @
    Madurai Travels
    Best Travels in Madurai
    Cabs in Madurai
    Tours and Travels in Madurai

    ReplyDelete
  9. Excellent Blog. I really want to admire the quality of this post. I like the way of your presentation of ideas, views and valuable content. No doubt you are doing great work. I’ll be waiting for your next post. Thanks .Keep it up!
    Kindly visit us @
    Luxury Packaging Box
    Wallet Box
    Perfume Box Manufacturer
    Candle Packaging Boxes
    Luxury Leather Box
    Luxury Clothes Box
    Luxury Cosmetics Box
    Shoe Box Manufacturer
    Luxury Watch Box

    ReplyDelete
  10. Wow, what an awesome spot to spend hours and hours! It's beautiful and I'm also surprised that you had it all to yourselves! Kindly visit us @ Best HIV Treatment in India | Top HIV Hospital in India | HIV AIDS Treatment in Mumbai
    HIV Specialist in Bangalore | HIV Positive Treatment in India | Medicine for AIDS in India

    ReplyDelete

EDI 837 P Budget Number Example