Operaciones CRUD en Liferay 7 con MVCPortlet y JSP

Aprende a realizar operaciones CRUD en Liferay 7 accediendo desde un MVCPortlet a la capa de servicio generada por Service Builder y creando la vista con JSP.

Índice de contenidos


1. Introducción

En el tutorial Persistencia en Liferay 7 con Service Builder, vimos cómo definir nuestro modelo, crear en base de datos las tablas correspondientes a él y generar automáticamente la capa de modelo, persistencia y servicio. Ahora vamos a hacer uso de la capa de servicio desde un portlet para poder realizar las operaciones CRUD básicas sobre nuestro modelo compuesto por libros y escritores: crearlos, listarlos, modificarlos y eliminarlos.

Este tutorial es una continuación del antes mencionado, siendo su lectura obligatoria. No obstante, no es necesario realizar los pasos llevados a cabo en él, pues daremos las indicaciones para crear un proyecto de cero.

Puedes encontrar el código de este tutorial en este repositorio. He ido separando en diferentes commits los pasos que se dan en el tutorial para así facilitar el seguimiento del mismo.


2. Entorno

Este tutorial se ha desarrollado en el siguiente entorno:

  • Portátil MacBook Pro (Retina, 15′, mediados 2015), macOS Sierra 10.12.5
  • Liferay Community Edition Portal 7.0.2 GA3 (Wilberforce / Build 7002 / August 5, 2016)
  • Java 1.8.0_131
  • PostgreSQL 9.6.2
  • IntelliJ IDEA Ultimate 2017.1.3

3. Preparar el proyecto

Si seguiste el tutorial de Service Builder, entonces ya tendrás un proyecto preparado con entidades Libro y Escritor relacionadas de forma M-N: un libro puede ser escrito por varios escritores y un escritor puede escribir diferentes libros. Si no lo tienes, sigue una serie de pasos para generar un proyecto similar al del tutorial de Service Builder, con la salvedad de que no tendremos clases de actualización de base de datos (UpgradeProcess y UpgradeStepRegistrator), ya que generaremos el modelo final de primeras y no haremos cambios sobre él, es decir, nuestro service.xml no cambiará.

Para preparar el proyecto, empieza abriendo la terminal y sigue los siguientes pasos:

  1. Creamos el proyecto Liferay:
  2. Añadimos el paquete Liferay Portal + Tomcat:
  3. Arrancamos el servidor local:
  4. Una vez se haya levantado, accedemos a http://localhost:8080/ y vemos el asistente de configuración de Liferay Portal. Como vamos a persistir en base de datos, no usaremos Hypersonic, sino que emplearemos otra base de datos (puedes seguir el tutorial Configurar Liferay 7 con PostgreSQL para ello).
  5. Tras haber configurado Liferay Portal y accedido a él (nos habrá pedido reiniciarlo al elegir otra base de datos como PostgreSQL), utilizamos la plantilla service-builder de Blade CLI para generar los módulos libro-api y libro-service:
  6. Creamos, desde el directorio libro, el módulo libro-web, donde tendremos nuestro portlet MVC:
  7. Abrimos el archivo bnd.bnd del módulo libro-web y cambiamos la línea: por:
  8. Modificamos el archivo service.xml:
  9. Generamos el código con Service Builder:
  10. Añadimos el módulo libro-api como dependencia de libro-web, para lo cual incluimos la línea compileOnly project(":modules:libro:libro-api") en el archivo build.gradle del módulo libro-web.
  11. Desplegamos los módulos: Si nos dio algún error del tipo «Unresolved requirement: Import-Package: tutoriales.liferay.crud.libro.exception», volvemos a ejecutar blade deploy. Este error es debido a que blade deploy despliega, en orden, libro-service, libro-web y libro-api y, como libro-service depende de libro-api, no puede desplegarlo. La segunda vez que ejecutamos el comando, como ya está desplegado libro-api, no tenemos problema. Si esto no funcionase, podemos ejecutar gradle clean && gradle build && gradle deploy en cada módulo y volver a probar.

Si accedemos a Liferay Portal, podemos buscar el portlet por el nombre «libro-web Portlet» y añadirlo a nuestro portal (puedes ver cómo hacerlo en el tutorial sobre Blade CLI). En este momento, el portlet simplemente mostrará los textos «libro-web Portlet» y «Hello from libro-web JSP!».


4. Analizar el módulo libro-web

Tras usar Blade CLI para crear este módulo, en el paquete java se generó únicamente la clase MyMvcPortlet. En resources, el archivo Language.properties y los JSP init.jsp y view.jsp. Muy pocos archivos en comparación con todos los que generó Service Builder en los módulos libro-api y libro-service.

Si seguimos el patrón de diseño MVC (Modelo-Vista-Controlador), nuestro modelo fue el que generamos con Service Builder; la vista, los JSP; y el controlador, el portlet MyMvcPortlet. Nuestro portlet recibirá peticiones de la vista, operará con el modelo y responderá a la vista.

4.1. MVCPortlet

MyMvcPortlet extiende MVCPortlet, que es la clase que Liferay nos insta a emplear, y está anotada con @Component para indicar que es un servicio declarativo de OSGi. Sus elementos son:

  • immediate = true. Indica que el componente se active inmediatamente después de ser instalado.
  • service = Portlet.class. Especifica el tipo bajo el cual registrar este componente como servicio. En nuestro caso será javax.portlet.Portlet.
  • property = {...}. Conjunto de propiedades del componente. Explicaremos algunas más adelante.

En principio, la lógica del controlador será sencilla, así que tendremos todo nuestro código en la clase MyMvcPortlet. Posteriormente lo separaremos en comandos MVC de Liferay.

4.2. JSP

Los archivos JSP formarán nuestra vista. Liferay nos dice que es una buena práctica que el archivo init.jsp contenga todos nuestros import de Java, declaraciones taglib e inicialización de variables.

init.jsp es incluido por view.jsp, que es el JSP que representa la vista del portlet. Pero ¿dónde se establece que esto sea así? En la clase MyMvcPortlet. Si nos fijamos en la propiedad javax.portlet.init-param.view-template, veremos que su valor es /view.jsp.

4.3. Language.properties

El archivo Language.properties está compuesto de pares nombre=valor y sirve para definir los textos que aparecen en nuestro portlet. Gracias a él podemos internacionalizar nuestro portlet, pues podemos crear archivos Language_en.properties, Language_es.properties, Language_fr.properties, etc. en los que incluir los textos en diferentes idiomas.


5. Realizar operaciones CRUD

Vamos a dar funcionalidad a nuestro portlet para que sea capaz de realizar operaciones CRUD sobre el modelo de libros y escritores definido. De esta manera, aprenderemos cómo desarrollar con portlets MVC y vista en JSP y cómo acceder desde el portlet al código generado por Service Builder.

5.1. [C] Crear

Queremos que nuestro portlet permita guardar escritores en base de datos. Tendrá un campo para escribir el nombre y un botón para guardar. La clase MyMvcPortlet recibirá el nombre del escritor y empleará la capa de servicio generada por Service Builder para añadir un nuevo escritor a base de datos.

En el tutorial Persistencia en Liferay 7 con Service Builder, vimos que las clases LibroLocalServiceUtil y EscritorLocalServiceUtil son nuestro punto de entrada a la capa de servicio. Para añadir un escritor desde MyMvcPortlet, tendríamos que usar EscritorLocalServiceUtil, pero vemos que su método addEscritor pide por parámetro un objeto Escritor. Escritor es una interfaz implementada por EscritorImpl, pero esta implementación es del módulo libro-service, del cual libro-web no depende —y queremos mantener esto así—, así que no podemos crear un Escritor desde MyMvcPortlet. ¿Qué hacemos entonces? Pues, básicamente, añadir un método a EscritorLocalServiceUtil que nos permita añadir escritores a partir de su nombre —y de un par de campos de multitenencia y auditoría—.

Vale, tenemos que añadir el nuevo método a EscritorLocalServiceUtil, pero esta clase es de libro-api, así que no debemos escribir nuestros propios métodos ahí. Lo que hay que hacer es desarrollar nuestro método en EscritorLocalServiceImpl (una de las poquitas clases que podemos editar) y, con Service Builder, regenerar EscritorLocalServiceUtil para que lo añada a ella y así podamos utilizarlo desde nuestro portlet. Vamos a ello.

Empezamos añadiendo el código a nuestra clase EscritorLocalServiceImpl:

Detalles a tener en cuenta:

  • Parámetros del método. Recordemos que nuestro Escritor tenía una serie de campos además del nombre, por lo que tendremos que especificarlos, aunque no todos: no hace falta preocuparse por los atributos createDate y modifiedDate (cuándo se creo y cuándo se modificó el escritor), pues estos se asignan automáticamente; sin embargo, no nos libramos de establecer cuál es el groupId y el companyId (identificadores del sitio y de la instancia del portal, respectivamente) y el userId y userName (identificador y nombre del usuario que crea el escritor).
  • Creación de Escritor. Aquí ya podemos importar EscritorImpl para crear una instancia de Escritor.
  • Adición de Escritor. Una vez construimos el escritor, lo añadimos a base de datos con el método addEscritor que ya tenía la clase.

Ahora ejecutamos Service Builder para generar el método en EscritorLocalServiceUtil:

Ya podemos usar el método desde nuestro portlet. Veamos su código primero:

Hemos creado un método addEscritor anotado con @ProcessAction y un atributo name con valor "addEscritor". Esta anotación sirve para indicar que el método addEscritor será el que se ejecute cuando desde la vista (desde JSP) se realice una petición al servidor para procesar la acción “addEscritor”. Por cierto, el nombre de la acción y el nombre del método no tienen por qué ser iguales.

Cuando el portlet reciba la petición de ejecutar esta acción, creará un escritor. Para ello necesita su nombre, y este viene dado por la vista en la petición, en el parámetro cuyo nombre es "nombreEscritor". Además del nombre, el método EscritorLocalServiceUtil.addEscritor necesita otra serie de campos. Afortunadamente, estos los podemos obtener de la petición creando un objeto ThemeDisplay.

Ahora nos queda definir la vista en el archivo view.jsp. Vamos a tener un formulario con un campo de texto en el que introducir el nombre del escritor y un botón para crear dicho escritor.

Analicemos el código. Por una parte, creamos una variable actionURL, llamada addEscritorUrl, que apunta a "addEscritor", el nombre de la acción para indicar al portlet que cree un escritor (recordemos la anotación @ProcessAction(name = "addEscritor")). Después, creamos un formulario al que le pasamos la variable addEscritorUrl para que sepa dónde mandar los datos. Dentro del formulario tenemos un botón y un campo de texto en el que escribir el nombre del autor. El nombre de este campo ( "nombreEscritor") es el que usamos en el portlet, el que vimos que utilizábamos para recoger su valor de la petición ( ParamUtil.getString(request, "nombreEscritor");).

Con el código listo, desplegamos con blade deploy y accedemos a Liferay Portal para ver nuestro portlet con los nuevos cambios. Seguramente te pase como a mí y veas el portlet, aparentemente, correcto:

Sin embargo, si intentas añadir un escritor, obtendrás una excepción java.lang.NoSuchMethodError y el portlet fallará:

Para solucionarlo, ejecuta gradle clean && gradle build && gradle deploy en cada módulo (al menos en libro-api), recarga la página y vuelve a probar:

5.2. [R] Leer

El siguiente paso tras crear escritores es poder listarlos: realizar una operación de lectura de base de datos y pintar la colección de escritores recibida en la vista.

Para la creación de escritores, el flujo consistía en que la vista mandaba al portlet el nombre de un escritor y éste hacía uso de la capa de servicio para crear uno nuevo y guardarlo en base de datos. Ahora el sentido será el contrario: cuando se vaya a pintar el portlet, éste hará una petición a base de datos para recuperar todos los escritores y se los mandará a la vista para que los pinte.

Empezamos añadiendo a nuestra clase MyMvcPortlet el siguiente método:

¿Para qué sirve el método render que sobreescribimos? Antes de nada, recordemos que las propiedades "javax.portlet.init-param.template-path=/" y "javax.portlet.init-param.view-template=/view.jsp" de nuestro portlet sirven para saber con qué vista se pinta el portlet: indican, respectivamente, que los JSP se encuentran en la raíz del directorio resources y que aquel que hará de vista será view.jsp. Pues bien, en nuestro caso no nos basta con que se pinte directamente el portlet a partir del archivo view.jsp; antes debemos pasarle la lista de escritores. Ahí es cuando entra en juego el método render.

En el método render, usamos EscritorLocalServiceUtil.getEscritors para recuperar todos los escritores y los añadimos al objeto RenderRequest como atributo de nombre "escritores".

Ahora modificamos el archivo view.jsp para añadir lo siguiente:

En la primera línea, recogemos la lista de escritores que mandamos desde el portlet. A continuación, empleamos el Liferay’s Search Container ( search-container) para crear una tabla en la que listar los escritores, aunque de momento solo va a tener una columna para mostrar sus nombres. Analicemos su código:

  • El atributo emptyResultsMessage nos permite definir el texto que se pintará si la lista de escritores es vacía.
  • En search-container-results indicamos la lista que vamos a pintar: results="${escritores}".
  • Con search-container-row aclaramos cuál es la clase de los elementos de nuestra lista: tutoriales.liferay.crud.libro.model.Escritor.
  • Definimos la columna con search-container-column-text. El atributo name es el nombre de la columna y el atributo property es el campo de la clase Escritor que va en ella.

Por cierto, el componente search-container proporciona funcionalidad extra, como paginación, que queda fuera de este tutorial y no usaremos, pero está bien saberlo.

Desplegamos y vemos nuestra lista de escritores:

5.3. [U] Actualizar

Para poder actualizar el nombre de un escritor, vamos a añadir a la tabla una nueva columna que tenga un botón de edición. Al pulsarlo, éste repintará el portlet —cambiará el JSP— para mostrar un campo de texto en el que poner el nuevo nombre y un botón para confirmar los cambios. Cuando se confirme, el portlet pintará su vista inicial, la del listado de escritores —volverá al JSP inicial—. De esta manera, aprenderemos además lo que hay que hacer para que el portlet cambie entre diferentes JSP.

5.3.1. Redirigir al formulario de edición

Empezamos añadiendo una nueva columna a nuestro search-container del archivo view.jsp:

Esta columna es de tipo search-container-column-jsp, lo que quiere decir que incluirá el JSP que indiquemos en su atributo path. El nuevo archivo escritorActionButtons.jsp es el siguiente:

Este JSP pinta un icono con imagen "edit" que, al ser pulsado, realiza la acción "displayEscritorEdition" guardada en la variable "displayEscritorEditionUrl". Esta acción, que ahora añadiremos a MyMvcPortlet para que la procese, consistirá en que el portlet cambie su vista view.jsp por una nueva vista escritorEdit.jsp en la que haya un campo para poder editar el nombre del escritor. Como tenemos que saber qué escritor queremos editar, habrá que mandar el identificador del mismo. Esto lo hacemos con la línea <portlet:param name="idEscritor" value="<%=String.valueOf(escritor.getEscritorId())%>"/>. El escritor lo sacamos de la petición, de su atributo WebKeys.SEARCH_CONTAINER_RESULT_ROW.

Añadimos la acción "displayEscritorEdition" a MyMvcPortlet:

El método recupera el escritor a partir de su identificador, lo añade a la request y redirige a escritorEdit.jsp. Eso último se hace añadiendo a la response el parámetro "mvcPath" —convención de Liferay— con valor la ruta del JSP.

5.3.2. Crear formulario de edición

Creamos la vista escritorEdit.jsp:

Este JSP recibe el escritor ( useBean), crea una variable en la que guarda una acción para indicar al portlet que debe editar un escritor ( actionURL) y pinta un formulario ( form) con un campo de texto en el que escribir el nombre ( "nombreEscritor"), un botón para realizar la acción y un campo de texto oculto ( "idEscritor") en el que guarda el identificador del escritor para así mandárselo al portlet y que éste sepa qué escritor modificar.

Ahora nos queda añadir a MyMvcPortlet el método para procesar la acción de edición. Este método va a llamar a la capa de servicio para actualizar base de datos, y aquí nos pasa algo parecido a lo que nos ocurría con la creación: el método updateEscritor de EscritorLocalServiceUtil recibe por parámetro un objeto Escritor que no podemos construir desde el portlet. Procedemos, por tanto, de la misma manera que antes: creamos en EscritorLocalServiceImpl un método de actualización a partir del id y nombre del escritor, generamos el código con ./gradlew buildService y usamos el método generado desde nuestro portlet.

El método de actualización en EscritorLocalServiceImpl es:

Tras generar el código con Service Builder, creamos en nuestra clase MyMvcPortlet un método que responderá a la petición de ejecución de la acción "editEscritor":

La acción recupera el identificador y el nuevo nombre del escritor, se los envía al método que acabamos de crear para que actualice y, por último, redirige a la vista inicial ( view.jsp) utilizando el, ya visto, parámetro "mvcPath".

Desplegamos (recuerda hacer gradle clean && gradle build && gradle deploy en, al menos, libro-api si te falla) y así quedaría nuestro portlet:

5.4. [D] Eliminar

La última operación es la de borrado y, con todo lo que llevamos ya montado, implementarla será fácil.

Vamos a aprovechar la columna en la que pusimos el botón de edición para poner también el de borrado, por lo que podemos empezar cambiándole el nombre, pues la llamamos «Editar». Ahora modificamos nuestro archivo escritorActionButtons.jsp para añadirle la URL a la acción de borrado ( "deleteEscritor") y el nuevo botón:

Escribimos el método de borrado en nuestra clase MyMvcPortlet:

Desplegamos y ya podemos borrar escritores:


6. Simplificar el controlador con comandos MVC

Veamos cómo ha quedado nuestro controlador:

Es una clase que realiza pocas operaciones y no es muy larga. Pero ¿qué ocurriría si nuestro portlet soportase más funcionalidad? Seguramente necesitaríamos más métodos anotados con @ProcessAction(name = "nueva_acción") y estos serían más complejos y tendrían más líneas de código. La clase iría creciendo, empeorando así la legibilidad. Para solucionar esto, Liferay ha creado comandos MVC que nos permiten aliviar el código de MVCPortlet: MVC Action Command, MVC Render Command y MVC Resource Command.

6.1. MVC Action Command

Los métodos de nuestra clase MyMvcPortlet podemos transformarlos en MVC Action Commands. Liferay proporciona la interfaz MVCActionCommand y una clase BaseMVCActionCommand que la implementa, por lo que nosotros deberemos extender BaseMVCActionCommand en lugar de implementar MVCActionCommand.

Por supuesto, puedes nombrar como desess a las clases que creemos, pero es buena práctica llamarlas «XXXMvcActionCommand», donde «XXX» es el nombre de la acción. Por ejemplo, si transformamos el método encargado de añadir escritores, como lo nombramos «addEscritor», tiene sentido que creemos una clase llamada «AddEscritorMvcActionCommand»:

El método addEscritor de MyMvcPortlet lo eliminamos y ponemos su contenido en nuestra nueva clase, en el método doProcessAction. Esta clase está anotada como componente de OSGi y sus propiedades son esenciales:

  • javax.portlet.name. Aquí tendremos que poner el nombre de nuestro portlet, que será aquel definido en MyMvcPortlet, en la propiedad homónima. Es decir, la propiedad "javax.portlet.name=tutoriales_liferay_crud_libro_portlet_MyMvcPortlet" deberá estar tanto en MyMvcPortlet como en AddEscritorMvcActionCommand: la primera identifica al portlet, la segunda lo referencia.
  • mvc.command.name. Esta propiedad define el nombre de la acción a procesar. Debe ser el que teníamos en la ya borrada anotación @ProcessAction.

Haremos lo mismo con las acciones «editEscritor» y «deleteEscritor».

6.2. MVC Render Command

En nuestra clase MyMvcPortlet ya solamente quedan dos métodos: render y displayEscritorEdition. Recordemos que este último, anotado con @ProcessAction, recibía la petición "displayEscritorEdition" que venía del actionURL de escritorActionButtons.jsp y su propósito era redirigir a escritorEdit.jsp. Vamos a editar el archivo JSP para transformar la actionURL en una renderURL:

Como vemos, es muy parecida a la antigua actionURL, solo que contiene un parámetro nombrado "mvcRenderCommandName" que indica cuál va a ser el componente MVCRenderCommand que procese la petición. MVCRenderCommand es una interfaz que nosotros tenemos que implementar:

Como vemos, de nuevo indicamos el nombre del portlet a través de la propiedad javax.portlet.name y el nombre del comando con mvc.command.name, nombre que indicamos en renderURL.

Por último, borramos el método displayEscritorEdition de MyMvcPortlet, clase que ya quedará así de simple:

6.3. MVC Resource Command

El último de los comandos, cuya interfaz es MVCResourceCommand y está implementado por la clase abstracta BaseMVCResourceCommand, es utilizado para procesar peticiones a recursos.

Vamos a dar la opción de descargar los escritores existentes. El usuario pulsará un botón y el navegador le ofrecerá la posibilidad de abrir o descargar un archivo de texto plano con los datos de los escritores que estén guardados en base de datos. Para ello, empezamos creando nuestro comando:

El método recoge los escritores de base de datos, hace un simple toString de ellos (nada más sofisticado para no meter ruido en lo que de verdad nos interesa en este momento) y crea un InputStream a partir de él. Con el método de utilidad PortletResponseUtil.sendFile somos capaces de hacer que el navegador nos permita descargar un archivo de texto con esta información. Por cierto, vemos que las propiedades de este comando —en la anotación @Component— son las mismas que debíamos definir en los otros dos comandos.

Al archivo view.jsp le añadimos el siguiente código:

Lo que hemos hecho ha sido definir una resourceURL cuyo identificador es el nombre del comando que hemos creado y un botón que nos permita ejecutar la petición.

Si desplegamos, podemos ver en nuestro portlet el nuevo botón de descarga y comprobar que todas las operaciones CRUD que realizábamos antes de crear los comandos siguen funcionando.


7. Trabajar con relaciones M-N

Ya hemos visto cómo hacer para comunicar la vista y el controlador, es decir, cómo se hablan los JSP con el MVCPortlet y sus comandos. También hemos visto cómo emplear la capa de servicio generada por Service Builder y añadir métodos personalizados. Sin embargo, todo esto lo hemos hecho únicamente con la entidad Escritor, ignorando completamente a Libro. Esto ha sido así porque estábamos centrados en aprender los conceptos mencionados, y meter más código hubiese sido añadir complejidad que no tenía que ver con la lección. Ahora, ya aprendido el tema, vamos a ver cómo guardar libros y escritores y que estos estén relacionados.

La funcionalidad a implementar que tendrá nuestro portlet será:

  • [C] Crear libros teniendo que elegir los escritores de cada uno.
  • [R] Listar libros junto a sus escritores.
  • [U] Editar los escritores de un libro.
  • [D] Eliminar libros y la relación con sus escritores y eliminar escritores y la relación con sus libros.

Los escritores con los que estábamos operando se persisten en la tabla LIBRO_Escritor. Además de esta tabla, recordemos que teníamos otras dos: LIBRO_Libro y LIBRO_Libros_Escritores. En la primera se guardan los libros y en la segunda los identificadores de los libros y de los escritores para saber quiénes son los autores de cada libro y qué libros ha escrito cada uno.

Para manejar la persistencia de escritores, implementábamos nuestros métodos personalizados en EscritorLocalServiceImpl y los usábamos, junto a los ya existentes, a través de la clase EscritorLocalServiceUtil desde MyMvcPortlet y sus comandos. Haremos lo propio para los libros con las clases LibroLocalServiceImpl y LibroLocalServiceUtil. ¿Y qué pasa con la relación? No existe un LibrosEscritoresLocalServiceUtil, sino que, al definir la relación M-N en el service.xml y generar el código con Service Builder, se generaron métodos en EscritorLocalServiceUtil y en LibroLocalServiceUtil que nos permiten operar con los identificadores de los libros y de los escritores. En concreto, emplearemos el método public static void setLibroEscritors(long libroId, long[] escritorIds) de la clase EscritorLocalServiceUtil:

En el método addLibro, primero creamos y añadimos el nuevo libro. Después empleamos el método de EscritorLocalServiceUtil antes mencionado para pasarle el identificador del libro y de los escritores que lo han compuesto; con esto, se añadirán nuevas filas a nuestra tabla LIBRO_Libros_Escritores.

Si queremos modificar quiénes son los escritores de un libro, volveremos a utilizar el mismo método, que se encargará de dejar en LIBRO_Libros_Escritores, para el libro dado, únicamente los identificadores de los escritores que pasamos al método.

Para listar los libros y los escritores de cada uno, utilizamos el siguiente código (que usaríamos en el método render de nuestra clase MyMvcPortlet):

  • Para recoger todos los libros:
  • Para obtener los escritores de un libro dado:

Ahora nos queda eliminar filas de LIBRO_Libros_Escritores, y esto no puede ser más fácil: no hay que hacer nada. Basta con que borremos un escritor o un libro para que se borren las filas de la relación que hacen referencia a él, es decir, emplearíamos el método EscritorLocalServiceUtil.deleteEscritor(long escritorId) o LibroLocalServiceUtil.deleteLibro(long libroId) y fin.

El código JSP y los comandos del controlador los omitimos para no hacer el tutorial más largo innecesariamente, pues no aportan nada nuevo a lo ya visto.


8. Conclusiones

En este tutorial hemos visto cómo la capa de servicio generada por Service Builder nos facilita la vida: da acceso a nuestro portlet MVCPortlet para realizar operaciones de persistencia como las CRUD y nos permite generar nuestros propios métodos, todo ello de manera simple cuando sabemos qué clases emplear.

Por otra parte, nos damos cuenta del interés de Liferay por modularizar su framework, llevándolo al desarrollo de portlets a través del cumplimiento del principio de responsabilidad única con los comandos MVC, que nos permiten aligerar nuestro MVCPortlet y definir en cada uno de ellos una acción del usuario.

Por último, hemos sido capaces de emplear la ya veterana tecnología JSP para crear nuestra vista y realizar la comunicación entre ella y los comandos MVC.


9. Referencias