Introduzione
Dopo aver introdotto i concetti base per sviluppare portlet custom, in questo articolo vedremo come fare qualcosa di più complicato, ma sicuramente più utile e interessante. Spiegheremo i concetti base per creare nel database le nostre tabelle, o meglio, illustreremo come farle creare al portale. Scopriremo come lo sviluppo di gran parte del codice per gestire i dati di queste tabelle sarà a carico del portale.
Service Builder
Il Service Builder [1] altro non è che un generatore di codice. Questa affermazione potrebbe sembrare riduttiva; alla fine dell'articolo capiremo quanto invece questo tool ci faciliti lo sviluppo. Ciò che viene generato dal Service Builder è l'insieme di classi e interfacce che servono per gestire i dati delle nostre tabelle. Prima di vedere come si utilizza, illustriamo brevemente come sono organizzate tra loro queste classi e interfacce. Diciamo innanzitutto che il codice generato può essere suddiviso in tre layer: model, service e persistence. Nel model rientrano le interfacce e le classi che, come dice la parola, rappresentato il nostro modello, ossia la nostra tabella. Nello strato persistence troviamo le classi che gestiscono la persistenza dei dati, quindi la creazione/cancellazione sul e dal DB, la lettura e l'aggiornamento dei dati. Infine c'è lo strato service, attraverso il quale potremo utilizzare le chiamate verso il persistence; non è infatti possibile fare le chiamate direttamente allo strato persistence. In Figura 1 è schematizzato quanto appena descritto.
Configurazione
Vediamo ora come si utilizza il Service Builder. Prima di tutto è necessario creare un Plugin Portlet, Gestione Libreria. Dentro questo plugin creiamo due portlet Gestione Autori e Gestione Libri che implementano la classe MVCPortlet. Apriamo liferay-portlet.xml e aggiungiamo queste righe:
<control-panel-entry-category>content</control-panel-entry-category> <control-panel-entry-weight>1.0</control-panel-entry-weight> e <instanceable>false</instanceable>
appena sotto <icon> per entrambe le portlet. Questo farà sì che queste verranno visualizzate nel pannello di controllo del portale. Immaginiamo, infatti, di creare delle portlet per il back-end. Se deployamo e avviamo il server, vedremo che nel pannello di controllo troviamo nell'elenco le nostre due portlet che per il momento visualizzano solamente una scritta creata di default dal Plugin SDK. A questo punto siamo pronti per creare la configurazione necessaria al Service Builder. Quello di cui abbiamo bisogno è un file XML, service.xml, da creare dentro la cartella WEB-INF del nostro Plugin Portlet. In questo file, tramite la sintassi espressa nella DTD, per il momento andremo a dichiarare le nostre tabelle e le colonne di cui sono composte.
<!DOCTYPE service-builder PUBLIC "-//Liferay//DTD Service Builder 6.0.0//EN" "http://www.liferay.com/dtd/liferay-service-builder_6_0_0.dtd"> <service-builder package-path="it.dvel.libreria"> <namespace>Libreria</namespace> <entity name="Libro" local-service="true" remote-service="true"> <!-- PK fields --> <column name="bookId" type="long" primary="true" /> <!-- Group instance --> <column name="groupId" type="long" /> <!-- Audit fields --> <column name="companyId" type="long" /> <column name="userId" type="long" /> <column name="userName" type="String" /> <column name="createDate" type="Date" /> <column name="modifiedDate" type="Date" /> <column name="authorId" type="long" /> <!-- Other fields --> <column name="title" type="String" /> </entity> <entity name="Autore" local-service="true" remote-service="true"> <!-- PK fields --> <column name="authorId" type="long" primary="true" /> <!-- Group instance --> <column name="groupId" type="long" /> <!-- Audit fields --> <column name="companyId" type="long" /> <column name="userId" type="long" /> <column name="userName" type="String" /> <column name="createDate" type="Date" /> <column name="modifiedDate" type="Date" /> <!-- Other fields --> <column name="name" type="String" /> <column name="surname" type="String" /> <!-- relationship --> <column name="books" type="Collection" entity="Libro" mapping-key="authorId" /> </entity> </service-builder>
Qualche nota
Analizziamo le varie righe: <service-builder package-path="it.dvel.libreria"> Qui dichiariamo il package che avranno tutte le classi e le interfacce generate. <namespace>Libreria</namespace> Questo è il prefisso che sarà usato per i nomi delle tabelle sul database. <entity name="Libro" local-service="true" remote-service="true"> Nome dell'entity e della tabella sul database. Se per la tabella vogliamo specificare un nome diverso dobbiamo utilizzare l'attributo table. local-service="true" remote-service="true" Dicono al Service Builder di creare le interfacce locali e remote rispettivamente. Dentro l'elemento entity andremo a specificare tutte le colonne con i rispettivi tipi tramite l'elemento column. Se dentro column mettiamo primary="true", allora quella colonna sarà utilizzata come primary key. Naturalmente è possibile indicare più colonne come primary key. Attenzione alla riga <column name="books" type="Collection" entity="Libro" mapping-key="authorId"/> Questa non crea effettivamente una colonna books nella tabella Autore. Serve a specificare una relazione 1-n tra la tabella Autore e quella Libro. In questo modo il Service Builder genera automaticamente i metodi per recuperare una lista di Libri dato l'id di un autore, poiché è quella la colonna che usiamo per la relazione nell'attributo mapping-key. Naturalmente è possibile esprimere anche relazioni n-n, in quel caso invece di mapping-key si usa l'attributo mapping-table.
Il risultato
Una volta creato il nostro file service.xml non ci resta che invocare il target build-service del nostro ant (figura 2).
Se facciamo refresh del progetto dentro Eclipse, vedremo il risultato dell'operazione appena fatta: dentro la src ci sono ora dei nuovi package e delle nuove classi, mentre dentro la cartella lib sotto WEB-INF c'è il JAR che contiene altre classi e interfacce. A questo punto facciamo deploy e facciamo ripartire il portale: sarà il portale stesso, in questo momento, a creare le tabelle definite nel file service.xml sul nostro database.
Esempi di applicazione
Vediamo ora come utilizzare le classi appena create e come aggiungere metodi di utility, sempre attraverso il Service Builder. Partiamo dalla view della portlet Gestione Libro. Vogliamo che nella view vengano visualizzati tutti i libri che ci sono sul database nella community in cui ci troviamo, in altre parole tutti i libri il cui groupId è uguale all'id che identifica la community (vedremo come si recupera applicativamente l'id della community corrente). Se guardiamo il file service.xml notiamo che abbiamo creato una colonna groupId <column name="groupId" type="long" /> che è proprio quella che ci interessa. Come facciamo a dire al Service Builder di creare per noi un metodo che, dato questo id, ci restituisca la lista dei libri? Lo facciamo sempre tramite il service.xml aggiungendo semplicemente queste righe subito dopo l'ultimo elemento column:
<finder name="GroupId" return-type="Collection"> <finder-column name="groupId" /> </finder>
L'attributo name dentro finder specifica il nome del metodo che sarà creato, return-type è il tipo di ritorno del metodo. Dentro finder-column indichiamo il nome della colonna da utilizzare per fare la nostra ricerca. Ora lanciamo di nuovo il target build-service: a tal proposito, ricordiamo che, in caso di errore del tipo [java] Exception in thread "main" java.lang.UnsupportedClassVersionError: Bad version number in .class file è bene effettuare un clean del progetto. Appena l'ant termina, facciamo di nuovo refresh del progetto. Se adesso guardiamo dentro l'interfaccia LibroPersistence, vedremo il metodo findByGroupId(long groupId), e una serie di metodi addizionali che il Service Builder crea automaticamente. Come abbiamo detto prima, però, non è possibile fare le chiamate direttamente verso lo strato persistence: per questo, se vogliamo utilizzare il finder appena creato, dobbiamo "esporlo" attraverso lo strato service. Per farlo, creiamo un metodo dentro LibroLocalServiceImpl: questa è l'unica classe che possiamo modificare e dove metteremo i nostri metodi custom. Aggiungiamo il seguente metodo:
public List<Libro> findByGroupId(long groupId) throws SystemException{
return libroPersistence.findByGroupId(groupId);
}
Lanciamo nuovamente il target build-service. Il Service Builder si accorgerà che abbiamo aggiunto un metodo dentro LibroLocalServiceImpl, la cui firma andrà ad aggiungere nell' interfaccia LibroLocalService. Dentro LibroLocalServiceUtil troviamo invece l'implementazione che non fa altro che invocare il metodo che abbiamo appena scritto. LibroLocalServiceUtil infatti è un wrapper della classe cui abbiamo appena aggiunto un metodo. Questo giro, apparentemente un po' complicato, ci permetterà di chiamare dalla nostra classe GestioneLibroPortlet il metodo LibroLocalServiceUtil.findByGroupId(groupId)che restituisce una lista di oggetti Libro. Utilizzando l'oggetto SearchContainer mostriamo nella view la lista dei risultati, analogamente a quanto fatto nel precedente articolo con gli utenti.
Inserire i dati
Naturalmente la lista sarà vuota poiché non abbiamo inserito nessun libro. Per farlo dobbiamo implementare la logica che ci permette di farlo. Prima però è necessario inserire almeno un autore poiché la tabella libro ha una colonna authorId che referenzia un autore. Vediamo come fare. Abbiamo bisogno quindi di un form per inserire Nome e Cognome .Questo è il codice JSP che renderizza la form:
<%@include file="/html/init.jsp"%> <jsp:useBean id="autore" type="it.dvel.libreria.model.Autore" scope="request"/> <portlet:actionURL name="updateAutore" var="updateAutoreURL" /> <aui:form name="fm" action="<%= updateAutoreURL.toString() %>" method="post"> <aui:fieldset> <aui:input name="resourcePrimKey" value="<%=autore.getAuthorId() %>" type="hidden" /> <liferay-ui:error key="title-required" message="title-required"/> <aui:input name="name" value="<%=autore.getName()%>" first="true" autoFocus="true" size="45" /> <aui:input name="surname" value="<%=autore.getSurname()%>" size="45" /> <aui:button-row> <aui:button type="submit" /> </aui:button-row> </aui:fieldset> </aui:form>
La prima riga è una direttiva per includere un'altra JSP nella quale abbiamo messo tutti gli import necessari, ed è mostrata parzialmente sotto:
<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %> <%@ taglib uri="http://liferay.com/tld/aui" prefix="aui" %> <%@ taglib uri="http://liferay.com/tld/portlet" prefix="liferay-portlet" %> <%@ taglib uri="http://liferay.com/tld/security" prefix="liferay-security" %> <%@ taglib uri="http://liferay.com/tld/theme" prefix="liferay-theme" %> <%@ taglib uri="http://liferay.com/tld/ui" prefix="liferay-ui" %> <%@ taglib uri="http://liferay.com/tld/util" prefix="liferay-util" %> <%@ page import="java.util.List" %> <%@ page import="java.util.Calendar" %> <%@ page import="java.util.Collections" %> . . . <%@ page import="it.dvel.libreria.model.Libro" %> <%@ page import="it.dvel.libreria.model.Autore" %> <%@ page import="javax.portlet.PortletURL" %> <portlet:defineObjects /> <liferay-theme:defineObjects />
Riprendiamo la JSP con la form e analizziamo le righe più importanti. In <jsp:useBean id="autore" type="it.dvel.libreria.model.Autore" scope="request" /> recuperiamo dalla request un oggetto tramite la chiave autore. È l'oggetto che rappresenta l'autore che vogliamo inserire: in questo caso è vuoto, poiché così l'abbiamo creato dentro GestioneAutoriPortlet; al contrario, sarà popolato nel caso in cui vogliamo modificare un autore. Questo ci permette di utilizzare la stessa JSP per inserimento e modifica. Infatti la riga <aui:input name="resourcePrimKey" value="<%=autore.getAuthorId() %>" type="hidden" /> crea un campo di input di tipo hidden che contiene l'id di un oggetto Autore. Per quanto detto sopra sarà 0 in caso di nuovo inserimento mentre sarà uguale all'id dell'autore sul database in caso di modifica. Il discorso della modifica sarà chiaro più avanti. Nella riga <portlet:actionURL name="updateAutore" var="updateAutoreURL" /> utilizziamo una taglib [2] per creare un actionURL che sarà richiamato dal nostro form al momento del submit: l'attributo name contiene il nome del metodo della nostra classe GestioneAutoriPortlet da invocare. Tutti i tag che invece iniziano per <aui: utilizzano la taglib Alloy UI [3] di Liferay per renderizzare i form. Ora che abbiamo la form dobbiamo fare in modo di visualizzarla nel portale: lo faremo tramite un pulsante dentro la JSP che mostra la lista degli autori: cliccandolo ci verrà mostrata la vista che contiene la form. Basta pochissimo codice: <portlet:actionURL name="addAutore" var="addAutoreURL" /> Questa riga crea l'URL da chiamare quando clicchiamo sul pulsante con la taglib di cui abbiamo parlato sopra. Ovviamente dentro la classe GestioneAutoriPortlet mi aspetto un metodo che si chiama addAutore. <input type="button" value="<liferay-ui:message key="add-Autore" />" onClick="location.href = '<%=addAutoreURL.toString() %>';" /> Questa riga mostra il nostro bottone. Sul click verrà chiamato l'URL che abbiamo creato nella riga precedente. Quella che segue, infine, è l'implementazione del metodo addAutore:
public void addAutore(ActionRequest request, ActionResponse response)
throws Exception {
//creiamo un oggeto AutoreImpl vuoto
AutoreImpl autore = new AutoreImpl();
//lo mettiamo in request,sarà recuperato nella JSP
//tramite <jsp:useBean che abbiamo descritto prima
request.setAttribute("autore", autore);
//dichiariamo quale JSP utilizzare per renderizzare
//il risultato.In questo caso è quella che mostra la form
response.setRenderParameter("jspPage",
"/html/gestioneautoriportlet/edit_autore.jsp");
}
In figura 3 vediamo il pulsante aggiunto in pagina.
Ed ecco il nostro form (figura 4).
Salvare i dati
Ora manca il codice che salva l'autore sul database, in particolare il metodo updateAutore richiamato al submit. Prima di vederlo. però. completiamo la parte che ci consente di modificare un autore già inserito.Il form c'è già, manca la logica che recupera i dati dal database e popola l'oggetto Autore. Tale logica è racchiusa nel seguente metodo dentro GestioneAutoriPortlet.
public void editAutore(ActionRequest request, ActionResponse response)
throws Exception {
//recupero dalla request l'id dell'autore che voglio modificare.
//Più avanti vedremo dove questo id sarà messo in request.
String primKey = ParamUtil.getString(request, "resourcePrimKey");
if (primKey != null) {
try {
//Recupero dal database i dati del mio autore, utlizzando
//uno dei metodi generati automaticamente dal
//Service Builder
Autore autore = AutoreLocalServiceUtil.getAutore(Long
.parseLong(primKey));
//metto l'oggetto autore in request
request.setAttribute("autore", autore);
//setto la JSP da utilizzare
response.setRenderParameter("jspPage",
"/html/gestioneautoriportlet/edit_autore.jsp");
} catch (NumberFormatException e) {
SessionErrors.add(request, "failed-to-retrieve");
} catch (PortalException e) {
SessionErrors.add(request, "failed-to-retrieve");
} catch (SystemException e) {
SessionErrors.add(request, "failed-to-retrieve");
}
}
}
Come dicevamo prima, ora l'oggetto Autore in request non è vuoto, quindi nella JSP del form, grazie a questa riga vista sopra <jsp:useBean id="autore" type="it.dvel.libreria.model.Autore" scope="request"/> troveremo i campi popolati, compreso resourcePrimKey, il quale conterrà l'id.
Vediamo ora dove viene invocato il metodo appena descritto. In figura 5 vediamo il particolare del pulsante "Action" mostrato in ogni riga della lista di autori, come si vedeva in figura 3.
Quando clicchiamo sul tasto Action si apre un menù che presenta due azioni disponibili: Delete e Edit. In particolare Edit ci permette di recuperare i dati dell'autore selezionato e di mostrarli nella form. Questo menu viene renderizzato grazie a una JSP inclusa direttamente da codice quando si popola l'oggetto Search Container, nel metodo doView, analogamente a come abbiamo visto nel precedente articolo. L'unica cosa che cambia è proprio la JSP inclusa, che è questa:
<%@include file="/html/init.jsp" %> <% ResultRow row = (ResultRow) request.getAttribute(WebKeys.SEARCH_CONTAINER_RESULT_ROW); Autore myAutore = (Autore)row.getObject(); long groupId = themeDisplay.getLayout().getGroupId(); String name = Autore.class.getName(); String primKey = String.valueOf(myAutore.getAuthorId()); %> <liferay-ui:icon-menu> <c:if test="<%= permissionChecker.hasPermission(groupId, name, primKey, ActionKeys.UPDATE) %>"> <portlet:actionURL name="editAutore" var="editURL"> <portlet:param name="resourcePrimKey" value="<%=primKey %>" /> </portlet:actionURL> <liferay-ui:icon image="edit" message="Edit" url="<%=editURL.toString() %>"/> </c:if> <c:if test="<%= permissionChecker.hasPermission(groupId, name, primKey, ActionKeys.DELETE) %>"> <portlet:actionURL name="deleteAutore" var="deleteURL"> <portlet:param name="resourcePrimKey" value="<%= primKey %>"/> </portlet:actionURL> <liferay-ui:icon-delete url="<%=deleteURL.toString() %>" /> </c:if> </liferay-ui:icon-menu>
Utilizzando un'altra taglib, Liferay UI [4], creare il menù è piuttosto semplice. Il primo pulsante che viene aggiunto è quello per la modifica, il cui URL punterà proprio al metodo editAutore visto sopra, al quale passerà come parametro il resourcePrimKey che utilizzeremo per fare la get dal database dei dati. Il secondo pulsante serve per la cancellazione, richiama il metodo deleteAutore dentro GestioneAutoriPortlet mostrato qui sotto.
public void deleteAutore(ActionRequest request, ActionResponse response)
throws Exception {
//prendiamo dalla request l'id
long autoreKey = ParamUtil.getLong(request, "resourcePrimKey");
if (Validator.isNotNull(autoreKey)) {
//se l'id non è nullo o vuoto invochiamo il metodo
//per la cancellazione, creato anch'esso dal
//Service Builder
AutoreLocalServiceUtil.deleteAutore(autoreKey);
SessionMessages.add(request, "autore-deleted");
} else {
SessionErrors.add(request, "error-deleting");
}
}
A questo punto per completare la gestione della tabella autori manca solo il metodo che fa l'inserimento o l'aggiornamento dei dati, mostrato qui sotto.
public void updateAutore(ActionRequest request, ActionResponse response)
throws PortletException {
Autore autore = null;
ThemeDisplay themeDisplay = (ThemeDisplay) request
.getAttribute(WebKeys.THEME_DISPLAY);
ArrayList<String> errors = new ArrayList();
//nel metodo di util autoreFromRequest andremo
//semplicemente a recuperare i parametri passati
//tramite la form
autore = AutoreUtil.autoreFromRequest(request);
long idAutore;
if (autore.getAuthorId() > 0) {
// se l'id passato è !=0 significa che abbiamo modificato
//un autore che già esiste sul DB, quindi si tratta di
//un update
try { //recuperiamo I dati che già si trovano sul DB
Autore fromDB = AutoreLocalServiceUtil.getAutore(autore
.getAuthorId());
if (fromDB != null
&& (autore.getAuthorId() == fromDB.getAuthorId())) {
autore.setModifiedDate(new Date());
autore.setCreateDate(fromDB.getCreateDate());
//facciamo l'update
fromDB = AutoreLocalServiceUtil.updateAutore(autore, false);
SessionMessages.add(request, "statement-added");
idAutore = fromDB.getAuthorId();
}
} catch (PortalException e) {
errors.add("failed-to-update");
} catch (SystemException e) {
errors.add("failed-to-update");
}
}
else {
//se entriamo qui vuol dire che stiamo facendo un nuovo inserimento
try {
//inseriamo
idAutore = (AutoreLocalServiceUtil.addAutore(autore.getUserId(),
autore.getName(), autore.getSurname(),
themeDisplay.getScopeGroupId())).getAuthorId();
} catch (SystemException e) {
errors.add("failed-to-add");
} catch (PortalException e) {
errors.add("failed-to-add");
}
}
if (errors.size() > 0) {
for (String error : errors) {
SessionErrors.add(request, error);
}
request.setAttribute("autore", autore);
response.setRenderParameter("jspPage",
"/html/gestioneautoriportlet/edit_autore.jsp");
}
}
La gestione per la tabella Autore è terminata. Torniamo alla tabella Libro sulla quale abbiamo lasciato in sospeso il discorso inserimento, dal momento che abbiamo detto di aver bisogno di almeno un autore al quale "assegnare" un libro. La logica e l'implementazione per tutte le operazioni sono quasi speculari a quelle viste per l'autore. L'unica differenza riguarda, appunto, l'inserimento. Il form per l'inserimento che vogliamo ottenere è mostrato in figura 6.
Come si vede c'è una select che è popolata con la lista degli autori. Nella nostra classe GestioneLibriPortlet scriviamo questo metodo:
private void addAutoriListToRequest(ActionRequest request)throws Exception {
ThemeDisplay themeDisplay =
(ThemeDisplay) request.getAttribute(WebKeys.THEME_DISPLAY);
//recuperiamo lo scope della community
long groupId = themeDisplay.getScopeGroupId();
//leggiamo dal DB tutti gli autori inseriti per la community corrente
List<Autore> autoriList = AutoreLocalServiceUtil.findByGroupId(groupId);
//mettiamo in request la lista appena recuperata
request.setAttribute("autoriList", autoriList);
}
Questo metodo sarà chiamato da addLibro e editLibro. Qui sotto vediamo la JSP che renderizza la form.
<%@include file="/html/init.jsp"%>
<jsp:useBean id="libro" type="it.dvel.libreria.model.Libro" scope="request" />
<jsp:useBean id="autoriList" class="java.util.ArrayList" type="java.util.List" scope="request" />
<portlet:actionURL name="updateLibro" var="updateLibroURL" >
<portlet:param name="" value=""></portlet:param>
</portlet:actionURL>
<aui:form name="fm" action="<%= updateLibroURL.toString() %>" method="post">
<aui:fieldset>
<aui:input name="resourcePrimKey" value="<%=libro.getBookId() %>"
type="hidden" />
<liferay-ui:error key="title-required" message="title-required" />
<aui:input name="title" value="<%=libro.getTitle()%>" first="true"
autoFocus="true" size="45" />
<aui:select label="Autore" name="authorId" showEmptyOption="true" >
<%
for (int i = 0; i < autoriList.size(); i++) {
Autore autore=(Autore)autoriList.get(i);
%>
<aui:option label="<%= autore.getName() +" " +autore.getSurname()%>"
selected="<%=autore.getAuthorId()==libro.getAuthorId() %>"
value="<%= autore.getAuthorId() %>" />
<%
}
%>
</aui:select>
<aui:button-row>
<aui:button type="submit" />
</aui:button-row>
</aui:fieldset>
</aui:form>
Soffermiamoci sul pezzo di codice che popola la tendina. Utilizziamo ancora le taglib Alloy UI. Per generare le option cicliamo sulla lista degli autori che è stata messa in request con il metodo addAutoriListToRequest di sopra. In particolare questa istruzione selected="<%=autore.getAuthorId()==libro.getAuthorId() %>" fa sì che, quando vogliamo modificare un libro, l'elemento della tendina selezionato di default è proprio quello corrispondente all'autore per il libro. Se adesso inseriamo un libro, questo avrà anche un autore!
Conclusioni
In questo articolo abbiamo descritto un tool fondamentale nello sviluppo di portlet per Liferay, il Service Builder. È un generatore di codice che produce, organizzate in una precisa struttura in livelli, le classi e le interfacce per gestire i dati su tabelle custom che creiamo sul database. I metodi base che crea sono quelli per inserimento, lettura, cancellazione e aggiornamento. Tutto quello di cui abbiamo bisogno è un file xml dove , con un preciso DTD, dichiariamo le tabelle che vogliamo creare e le rispettive colonne. Sarà il portale a creare gli script SQL e a eseguirli sul nostro database la prima volta che lo facciamo ripartire. Abbiamo visto poi come sia possibile affiancare dei metodi "finder" custom ai metodi di default. Nell'articolo sono serviti per recuperare libri e autori con un determinato community id; naturalmente non è l'unica applicazione possibile. Si possono ad esempio creare dei "finder" per fare delle ricerche sui campi titolo, nome o cognome. Inoltre è possibile, sempre dentro l'XML, specificare l'ordinamento dei dati dichiarando su quale colonna ordinare, e se in modo ascendente o discendente. Abbiamo anche visto come aggiungere della logica custom costruita sui metodi che già esistono: creando dei metodi dentro la classe <EntityName>LocalServiceImpl.java, il ServiceBuilder, una volta lanciato, crea dentro l'interfaccia <EntityName>LocalService.java un metodo con la stessa firma del nostro, e dentro <EntityName>LocalServiceUtil.java la sua implementazione. Questa classe in pratica altro non è che un wrapper per la <EntityName>LocalServiceImpl.java che contiene la nostra implementazione custom. Con quest'approccio è possibile quindi costruire query complesse per applicazioni più vaste e articolate risparmiando, oltretutto, una notevole quantità di tempo.
Riferimenti
- [1] Service Builder
http://www.liferay.com/community/wiki/-/wiki/Main/Service+Builder
- [2] JSR 286 tag library
http://www.oracle.com/technetwork/java/jsr286-2-141964.html#Tag_Library
- [3] Liferay Alloy UI
http://www.liferay.com/web/guest/community/wiki/-/wiki/Main/Alloy+UI+Forms+(aui)
- [4] Liferay UI
http://www.liferay.com/community/wiki/-/wiki/Main/Liferay+UI+Taglib
- [5] Service Builder tips
http://blog.d-vel.com/web/blog/home/-/blogs/query-su-colonne-unique:-come-configurare-il-servicebuilder
