Generating the Back-End
Step 2 of 3
To model the guestbooks and entries, you’ll create guestbook and entry model classes. But you won’t do this directly in Java. Instead, you’ll define them in Service Builder, which generates your object model and maps it to all the SQL databases Liferay DXP supports.
This application’s design allows for multiple guestbooks, each containing different sets of entries. All users with permission to access the application can add entries, but only administrative users can add guestbooks.
It’s time to get started. You’ll create the Guestbook
entity first:
-
In your
guestbook-service
project, openservice.xml
. Make sure the Source tab is selected. -
When Liferay Dev Studio DXP generated your project, it filled this file with dummy entities, which you’ll replace. Remove everything in the file below the
DOCTYPE
. Replace the file’s opening contents with the following code:<service-builder dependency-injector="ds" package-path="com.liferay.docs.guestbook" mvcc-enabled="true"> <author>liferay</author> <namespace>GB</namespace> <entity name="Guestbook" local-service="true" uuid="true" remote-service="true">
This defines the author, namespace, and the entity name. The namespace keeps the database field names from conflicting. The last tag is the opening tag for the
Guestbook
entity definition. In this tag, you enable local and remote services for the entity, define its name, and specify that it should have a universally unique identifier (UUID). -
The Guestbook requires only two fields: a primary key to identify it uniquely in the database, and a name. Add these fields:
<!-- Guestbook fields --> <column name="guestbookId" primary="true" type="long" /> <column name="name" type="String" />
This defines
guestbookId
as the entity’s primary key of the typelong
and the name as aString
. -
Next, define the group instance. The
groupId
defines the ID of the Site in Liferay DXP where the entity instance exists. ThecompanyId
is the primary key of a portal instance.<!-- Group instance --> <column name="groupId" type="long" /> <column name="companyId" type="long" />
-
Next, add audit fields. These fields help you track owners of entity instances, along with those instances’ create and modify dates:
<!-- Audit fields --> <column name="userId" type="long" /> <column name="userName" type="String" /> <column name="createDate" type="Date" /> <column name="modifiedDate" type="Date" />
-
After this, add fields that support Liferay’s workflow system. These provide fields in the database to track your entity’s status as it passes through the workflow.
<!-- Status fields --> <column name="status" type="int" /> <column name="statusByUserId" type="long" /> <column name="statusByUserName" type="String" /> <column name="statusDate" type="Date" />
-
Before the closing
</entity>
tag, add this finder definition:<finder name="GroupId" return-type="Collection"> <finder-column name="groupId" /> </finder> </entity>
A finder
generates a get
method for retrieving Guestbook entities. The fields used by
the finder define the scope of the data retrieved. This finder gets all
Guestbooks by their groupId
. This is how administrators put Guestbooks on
multiple Sites, and each Guestbook
has its own data scoped to its Site.
The Guestbook
entity is finished for now. Next, you’ll create the
GuestbookEntry
entity:
-
Add the opening entity tag:
<entity name="GuestbookEntry" local-service="true" remote-service="true" uuid="true">
As with the
Guestbook
entity, you enable local and remote services, define the entity’s name, and specify that it should have a UUID. -
Add the fields that define the
GuestbookEntry
’s data:<!-- Guestbook Entry fields --> <column name="entryId" primary="true" type="long" /> <column name="name" type="String" /> <column name="email" type="String" /> <column name="message" type="String" /> <column name="guestbookId" type="long" />
The
name
,email
, andmessage
fields comprise aGuestbookEntry
. These fields define the name of the person creating the entry, an email address, and the Guestbook message, respectively. TheguestbookId
is assigned automatically by code you’ll write, and is a foreign key to theGuestbook
where this entry belongs. -
Add fields to track the portal instance and group:
<!-- Group instance --> <column name="groupId" type="long" /> <column name="companyId" type="long" />
-
Add audit fields:
<!-- Audit fields --> <column name="userId" type="long" /> <column name="userName" type="String" /> <column name="createDate" type="Date" /> <column name="modifiedDate" type="Date" />
-
Add status fields to track workflow:
<!-- Status fields --> <column name="status" type="int" /> <column name="statusByUserId" type="long" /> <column name="statusByUserName" type="String" /> <column name="statusDate" type="Date" />
-
When querying for
GuestbookEntry
s, you can order them by one or more columns. Since visitors signGuestbook
s in order by time, order yourGuestbookEntry
instances by the date they were created:<order> <order-column name="createDate" order-by="desc" /> </order>
-
Add a finder that retrieves
GuestbooEntry
s by a combination ofgroupId
andguestbookId
. This supports Liferay DXP’s multi-tenancy by only returning those entries that belong both to the current Site and the current Guestbook. After defining your finder add the closing entity tag:<finder name="G_G" return-type="Collection"> <finder-column name="groupId" /> <finder-column name="guestbookId" /> </finder> </entity>
-
Define exception types outside the
<entity>
tags, just before the closing</service-builder>
tag:<exceptions> <exception>GuestbookEntryEmail</exception> <exception>GuestbookEntryMessage</exception> <exception>GuestbookEntryName</exception> <exception>GuestbookName</exception> </exceptions> </service-builder>
These generate exception classes you’ll use later in try/catch statements.
-
Save your
service.xml
file.
Now you’re ready to run Service Builder to generate your model, service, and persistence layers!
-
In the Gradle Tasks pane on the right side of Dev Studio DXP, open
com-liferay-docs-guestbook
→modules
→guestbook
→guestbook-service
→build
. -
Run
buildService
by right-clicking it and selecting Run Gradle Tasks. Make sure you’re connected to the Internet, as Gradle downloads dependencies the first time you run it. -
In the Project Explorer, right-click the
guestbook-service
module and select Refresh. Repeat this step for theguestbook-api
module. This ensures that the new classes and interfaces generated by Service Builder show up in Dev Studio DXP. -
In the Project Explorer, right-click the
guestbook-service
module and select Gradle → Refresh Gradle Project. Repeat this step for theguestbook-api
module. This ensures that your modules’ Gradle dependencies are up to date.
Service Builder is based on a design philosophy called loose coupling. It
generates three layers of your application: the model, the service, and the
persistence layers. Loose coupling means you can swap out the persistence layer
with little to no change in the model and service layers. The model is in the
-api
module, and the service and persistence layers are in the -service
module.
Figure 1: The Model, Service, and Persistence Layer comprise a loose coupling design.
Each layer is implemented using Java Interfaces and implementations of those
interfaces. Rather than have one Guestbook
class that represents your
model, Service Builder generates a system of classes that includes a Guestbook
interface, a GuestbookBaseImpl
abstract class that Service Builder manages,
and a GuestbookImpl
class that you can customize. With this design, you can
customize your model and let Service Builder generate the tedious-to-write
code. That’s why Service Builder is a code generator for code generator haters.
Next, you’ll create the service implementations.