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;
}
}
|