Working with Expando API in Liferay

Liferay have introduced Expando API to extending existed Liferay tables to add additional columns rather than creating new tables in the database.
 
Logically its look like new tables in Liferay but originally it won’t be created in the Liferay database.
 
All Expando mechanism is organized by following four tables which are already created in the Liferay database.
 
ExpandoTable
 
This will manage the brand new table information its means virtual tables information
 
ExpandoColumn
 
It manages the columns information related to respective Expando table.
 
ExpandoRow
 
It will used to manage table rows related to respective Expando table
 
ExpandoValue
 
 Originally data will be stored in this table and it will mapped with Expando table and Expando row.
From above four tables we can create many tables logically and which act like original tables in database.
 
Advantages:
 
We can extend existed Liferay tables
 
Liferay have implemented custom fields for User, Site, Role and Organization it will use the Expando mechanism to create additional fields so that it will be extending the original  User_, Role_, Organization_ and Group_ tables.
 
 
We can Logically Create brand new tables and columns
 
We can also create new tables and columns in our real time requirement and when we created tables and columns using Expando we don’t need to write any service layer implementation to data storage and retrieval and with help of Expando API classes we can store and retrieve data.
 
Best example for above is Webform Portlet. This will use Expando API to create brand new tables and columns to each form based on fields we have created for webform.
 
The data will be managed in brand new Expando table here for each form they will created tables and all form data will be stored in the respective Expando table.
 
Brand new table information available in ExpandoTable,
 
All columns information ExapndoColumn with mapping of Expando Table Id
 
For each form submission it will create new row in ExpandoRow table
 
Form data will be stored in ExpnadoValue and here for each input field data will create new record in ExpnadoValue table and stored with reference ids like ExpandoRow Id.
 
Liferay Expando API Implementation Example
 
Assume we are going to create employee form and employee consists of different fields.
Now we will use Expando API to store employee information and display in Portlet.
 
Employee Form Fields
 
 
First Name,
Last Name
Employee Designation
Email Address
Age
Phone Number
 
 
The following is Required Expando Artifacts in Implementation
 
Expando Table Name:  Employee
 
Expando Columns
 
Column Name
Type
First Name,
String
Last Name
String
Employee Designation
String
Email Address
String
Age
Integer
Phone Number
String
 
Required Steps in Implementation
 
Need to create Expando Table and Name is Employee
 
Need to create Expando Columns for Employee with reference of Expando Table Id
 
Need to store data in Expando Value table with reference of Table Id, Expando Row Id
 
Note:
 
We need to remember that Expando Tables and Its columns only for one time creation and each employee form submission we will create ExpandoRow and Add values in ExpandoValue Table. For each filed in Employee form we will create new ExpandoValue record and store field data in it.
 
Here we need not to create ExpandoRow explicitly for each employee form submission, at the time of add values in ExpandoValue table rows will be crated automatically in ExpandoRow Table.
The following are Expando API classes we will use in Code Implementation
 
 
From above classed we will use different API methods and the following are some of important method signatures
 
 
ExpandoTableLocalServiceUtil.addTable(companyId, XXXX.class.getName(), tableName);
 
ExpandoColumnLocalServiceUtil.addColumn(tableId,fieldName,ExpandoColumnConstants.STRING);
 
ExpandoColumnLocalServiceUtil.addColumn(tableId,fieldName,ExpandoColumnConstants.Integer);
 
ExpandoValueLocalServiceUtil.addValue(
companyId, XXXX.class.getName(), databaseTableName,
fieldLabel, classPK, fieldValue);
 
 
 
ExpandoColumnConstants provide values for column types  such as int, long, String, long, double and Boolean.
Note: 
ClassPK is kind of primary key we can generate as follows
 
long classPK = CounterLocalServiceUtil.increment(XXXX.class.getName());
OR
long classPK = CounterLocalServiceUtil.increment();
 
 
Database Reference for Expando API
 

 
Expando API Example Portlet View Page
 

 
Expando API Example Portlet Add Employee Page
 

 
Expando API Example Portlet Display Employees Page
 

Download Liferay Expando API Example Portlet
 
 
Complete Code Example
 
view.jsp (/html/jsps/view.jsp)
 
 
<%@ taglib uri= "http://liferay.com/tld/portlet" prefix= "liferay-portlet" %>
<%@ taglib uri= "http://liferay.com/tld/theme" prefix= "liferay-theme" %>
<%@ taglib uri= "http://liferay.com/tld/ui" prefix= "liferay-ui" %>
<%@ taglib uri= "http://java.sun.com/portlet_2_0" prefix= "portlet" %>
<portlet:defineObjects />
<liferay-theme:defineObjects />
<h1>Liferay Expando API  Example</h1>
<portlet:renderURL var= "addEmployee">
<portlet:param name= "mvcPath" value= "/html/jsps/add_employee.jsp"/>
</portlet:renderURL>
<portlet:renderURL var= "dislayEmployees">
<portlet:param name= "mvcPath" value= "/html/jsps/display_employeed.jsp"/>
</portlet:renderURL>
 
<br/>
<a href= "<%=addEmployee.toString()%> ">Add Employee</a><br/>
<a href= "<%=dislayEmployees.toString()%> ">Display Employees</a><br/>
 
 
add_employee.jsp  (/html/jsps/add_employee.jsp)
 
 
<%@page import= "com.liferay.portal.kernel.servlet.SessionErrors"%>
<%@page import= "com.liferay.portal.kernel.servlet.SessionMessages"%>
<%@ taglib uri= "http://liferay.com/tld/portlet" prefix= "liferay-portlet" %>
 
<%@ taglib uri= "http://liferay.com/tld/theme" prefix= "liferay-theme" %>
<%@ taglib uri= "http://liferay.com/tld/ui" prefix= "liferay-ui" %>
<%@ taglib uri= "http://java.sun.com/portlet_2_0" prefix= "portlet" %>
<portlet:defineObjects />
<portlet:renderURL var= "homeURL"></portlet:renderURL>
<portlet:actionURL var= "addEmployeeActionURL" windowState= "normal"
name= "addEmployee">
</portlet:actionURL>
<%  if(SessionMessages.contains(renderRequest.
getPortletSession(),"employee-add-success")){%>
<liferay-ui:success key= "employee-add-success" message= "Employee information
have been added successfully." />
<%} %>
<%  if(SessionErrors.contains(renderRequest.getPortletSession(),
"employee-add-error")){%>
<liferay-ui:error key= "employee-add-error" message= "There is an
Error occured while adding employee and please try again" />
<%} %>
<h2>Add Employee</h2>
<a href= "<%=homeURL.toString() %> ">Home</a><br/><br/>
<form action= "<%=addEmployeeActionURL%> " name= "employeeForm"  method= "POST">
<b>First Name</b><br/>
<input  type= "text" name= "<portlet:namespace/> employeeFirstName" id= "<portlet:namespace/> employeeFirstName"/><br/>
<b>Last Name</b><br/>
<input type= "text" name= "<portlet:namespace/> employeeLastName" id= "<portlet:namespace/> employeeLastName"/><br/>
<b>Employee Designation</b><br/>
<input type= "text" name= "<portlet:namespace/> employeeDesignation" id= "<portlet:namespace/> employeeDesignation"/><br/>
<b>Email Address</b><br/>
<input type= "text" name= "<portlet:namespace/> emailAddress" id= "<portlet:namespace/> emailAddress"/><br/>
<b>Age</b><br/>
<input type= "text" name= "<portlet:namespace/> employeeAge" id= "<portlet:namespace/> employeeAge"/><br/>
<b>Phone Number</b><br/>
<input type= "text" name= "<portlet:namespace/> phoneNumber" id= "<portlet:namespace/> phoneNumber"/><br/>
<input type= "submit" name= "addStudent" id= "addStudent" value= "Add Employee"/>
</form>
 
 
display_employeed.jsp  (/html/jsps/display_employeed.jsp)
 
 
<%@page import= "com.liferay.portlet.expando.NoSuchTableException"%>
<%@page import= "com.liferay.portlet.expando.model.ExpandoTable"%>
<%@page import= "com.liferay.portlet.expando.service.ExpandoTableLocalServiceUtil"%>
<%@page import= "com.liferay.portal.kernel.util.StringPool"%>
<%@page import= "com.liferay.portlet.expando.service.ExpandoValueLocalServiceUtil"%>
<%@page import= "com.liferay.portlet.expando.model.ExpandoRow"%>
<%@page import= "com.liferay.portal.kernel.dao.orm.QueryUtil"%>
<%@page import= "com.meera.liferay.expandoapi.LiferayExpandoAPIAction"%>
<%@page import= "java.util.List"%>
<%@page import= "com.liferay.portlet.expando.service.ExpandoRowLocalServiceUtil"%>
<%@ taglib uri= "http://liferay.com/tld/portlet" prefix= "liferay-portlet" %>
<%@ taglib uri= "http://liferay.com/tld/theme" prefix= "liferay-theme" %>
<%@ taglib uri= "http://liferay.com/tld/ui" prefix= "liferay-ui" %>
<%@ taglib uri= "http://java.sun.com/portlet_2_0" prefix= "portlet" %>
<portlet:defineObjects />
<liferay-theme:defineObjects />
<portlet:renderURL var= "homeURL"></portlet:renderURL>
<h1>Liferay Expando API Display Employees</h1>
<a href= "<%=homeURL.toString() %> ">Home</a><br/><br/>
<table style="width: 100%" border= "1">
<tr>
<th>FirstName</th>
<th>LastName</th>
<th>Designation</th>
<th>EmailAddress</th>
<th>Age</th>
<th>PhoneNumber</th>
</tr>
<%
ExpandoTable expandoTable= null;
String message= null;
try {
expandoTable = ExpandoTableLocalServiceUtil.getTable(
themeDisplay.getCompanyId(), LiferayExpandoAPIAction. class.getName(),LiferayExpandoAPIAction.expandoTableName);
}
catch (NoSuchTableException nste) {
message="Table  not existed to show the data. please add data first and comeback to to see the data";
}
 
if(expandoTable!= null){
List<ExpandoRow> rows = ExpandoRowLocalServiceUtil.getRows(
themeDisplay.getCompanyId(), LiferayExpandoAPIAction. class.getName(),
LiferayExpandoAPIAction.expandoTableName, QueryUtil.ALL_POS, QueryUtil.ALL_POS);
for (ExpandoRow row : rows) {
String data = ExpandoValueLocalServiceUtil.getData(
themeDisplay.getCompanyId(),
LiferayExpandoAPIAction. class.getName(), LiferayExpandoAPIAction.expandoTableName,
LiferayExpandoAPIAction.columnNames[0], row.getClassPK(), StringPool.BLANK);
 
%>
<tr>
<td><%=ExpandoValueLocalServiceUtil.getData(
themeDisplay.getCompanyId(),
LiferayExpandoAPIAction. class.getName(), LiferayExpandoAPIAction.expandoTableName,
LiferayExpandoAPIAction.columnNames[0], row.getClassPK(), StringPool.BLANK) %></td>
 
<td><%=ExpandoValueLocalServiceUtil.getData(
themeDisplay.getCompanyId(),
LiferayExpandoAPIAction. class.getName(), LiferayExpandoAPIAction.expandoTableName,
LiferayExpandoAPIAction.columnNames[1], row.getClassPK(), StringPool.BLANK) %></td>
<td><%=ExpandoValueLocalServiceUtil.getData(
themeDisplay.getCompanyId(),
LiferayExpandoAPIAction. class.getName(), LiferayExpandoAPIAction.expandoTableName,
LiferayExpandoAPIAction.columnNames[2], row.getClassPK(), StringPool.BLANK) %></td>
<td><%=ExpandoValueLocalServiceUtil.getData(
themeDisplay.getCompanyId(),
LiferayExpandoAPIAction. class.getName(), LiferayExpandoAPIAction.expandoTableName,
LiferayExpandoAPIAction.columnNames[3], row.getClassPK(), StringPool.BLANK) %></td>
<td><%=ExpandoValueLocalServiceUtil.getData(
themeDisplay.getCompanyId(),
LiferayExpandoAPIAction. class.getName(), LiferayExpandoAPIAction.expandoTableName,
LiferayExpandoAPIAction.columnNames[4], row.getClassPK(), 0) %></td>
<td><%=ExpandoValueLocalServiceUtil.getData(
themeDisplay.getCompanyId(),
LiferayExpandoAPIAction. class.getName(), LiferayExpandoAPIAction.expandoTableName,
LiferayExpandoAPIAction.columnNames[5], row.getClassPK(), StringPool.BLANK) %></td>
</tr>
<%}}%>
</table>
<h1><%=message!= null?message:StringPool.BLANK%></h1>
 
 
LiferayExpandoAPIAction.java
 
 
package com.meera.liferay.expandoapi;
import java.io.IOException;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.PortletException;
import com.liferay.counter.service.CounterLocalServiceUtil;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.exception.SystemException;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.servlet.SessionErrors;
import com.liferay.portal.kernel.servlet.SessionMessages;
import com.liferay.portal.kernel.util.ParamUtil;
import com.liferay.portal.kernel.util.WebKeys;
import com.liferay.portal.theme.ThemeDisplay;
import com.liferay.portlet.expando.NoSuchTableException;
import com.liferay.portlet.expando.model.ExpandoColumnConstants;
import com.liferay.portlet.expando.model.ExpandoTable;
import com.liferay.portlet.expando.service.ExpandoColumnLocalServiceUtil;
import com.liferay.portlet.expando.service.ExpandoTableLocalServiceUtil;
import com.liferay.portlet.expando.service.ExpandoValueLocalServiceUtil;
import com.liferay.util.bridges.mvc.MVCPortlet;
public  class LiferayExpandoAPIAction  extends MVCPortlet {
public  static String[]  columnNames={"FirstName","LastName","Designation","EmailAddress","Age","PhoneNumber"};
public  static String  expandoTableName="Employee";
private  static Log  _log = LogFactoryUtil. getLog(LiferayExpandoAPIAction. class);
public  void addEmployee(ActionRequest actionRequest,
ActionResponse actionResponse)  throws IOException, PortletException {
try {
boolean dataAdedSuccess= false;
String firstNameValue = ParamUtil. getString(actionRequest, "employeeFirstName");
String lastNameValue = ParamUtil. getString(actionRequest, "employeeLastName");
String employeeDesignation = ParamUtil. getString(actionRequest, "employeeDesignation");
String emailAddressValue = ParamUtil. getString(actionRequest, "emailAddress");
int employeeAgeValue = ParamUtil. getInteger(actionRequest, "employeeAge");
String phoneNumberValue = ParamUtil. getString(actionRequest, "phoneNumber");
ThemeDisplay themeDisplay = (ThemeDisplay)actionRequest.getAttribute(WebKeys. THEME_DISPLAY);
long companyId=themeDisplay.getCompanyId();
 
ExpandoTable expandoTable=checkTable(companyId, expandoTableName);
_log.info("expandoTable"+expandoTable.getTableId());
if(expandoTable!= null){
long classPK = CounterLocalServiceUtil. increment(LiferayExpandoAPIAction. class.getName());
ExpandoValueLocalServiceUtil. addValue(companyId, LiferayExpandoAPIAction. class.getName(), expandoTableName,
columnNames[0],classPK, firstNameValue);
ExpandoValueLocalServiceUtil. addValue(companyId, LiferayExpandoAPIAction. class.getName(), expandoTableName,
columnNames[1],classPK, lastNameValue);
ExpandoValueLocalServiceUtil. addValue(companyId, LiferayExpandoAPIAction. class.getName(), expandoTableName,
columnNames[2],classPK, employeeDesignation);
ExpandoValueLocalServiceUtil. addValue(companyId, LiferayExpandoAPIAction. class.getName(), expandoTableName,
columnNames[3],classPK, emailAddressValue);
ExpandoValueLocalServiceUtil. addValue(companyId, LiferayExpandoAPIAction. class.getName(), expandoTableName,
columnNames[4],classPK, employeeAgeValue);
ExpandoValueLocalServiceUtil. addValue(companyId, LiferayExpandoAPIAction. class.getName(), expandoTableName,
columnNames[5],classPK, phoneNumberValue);
dataAdedSuccess= true;
}
 
if (dataAdedSuccess) {
// adding success message
SessionMessages. add(actionRequest.getPortletSession(),
"employee-add-success");
_log.info("Employee have been added successfylly");
}
// navigate to add student jsp page
actionResponse.setRenderParameter("mvcPath",
"/html/jsps/add_employee.jsp");
catch (Exception e) {
SessionErrors. add(actionRequest.getPortletSession(),
"employee-add-error");
e.printStackTrace();
}
}
 
public  ExpandoTable addTable( long companyId, String tableName)
throws PortalException, SystemException {
ExpandoTable expandoTable=ExpandoTableLocalServiceUtil. addTable(
companyId, LiferayExpandoAPIAction. class.getName(), tableName);
_log.error("Expando Table Created Successfully.");
return expandoTable;
}
public  ExpandoTable checkTable( long companyId, String tableName)
throws Exception {
 
ExpandoTable expandoTable =  null;
 
try {
expandoTable = ExpandoTableLocalServiceUtil. getTable(
companyId, LiferayExpandoAPIAction. class.getName(), tableName);
}
catch (NoSuchTableException nste) {
expandoTable = addTable(companyId, tableName);
for(String columnName: columnNames){
if(columnName.equals("Age")){
ExpandoColumnLocalServiceUtil. addColumn(
expandoTable.getTableId(), columnName,
ExpandoColumnConstants. INTEGER);
} else{
ExpandoColumnLocalServiceUtil. addColumn(
expandoTable.getTableId(), columnName,
ExpandoColumnConstants. STRING);
}
 
_log.error("Expando Column"+columnName+"Created Successfully.");
}
 
}
return expandoTable;
}
}
 
2
Blogs