Liferay 7.2. Service Layer - Creando entidades y servicios
Sigamos con Liferay, ahora con el tema Service Layer.
Liferay Service Builder es una herramienta de generación de código que permite, usando un archivo xml, generar una capa completa de servicios. La generación de código incluye definciones de base de datos, código para caché y persistencia, clases de servicios con métodos CURD y la capa de servicios remotos con compatibilidad con JSON y SOAP Web Services.
¡Y todo esto con un solo archivo
En el archivo service.xml podemos definir lo siguiente:
virtual-classroom > modules > classroom > classrom-service > build > buildService
Doble clic, y esperemos a que termine de construir.
Al terminar, presionamos Ctrl+F5 en el árbol del proyecto, y veremos que se crearon clases.
En clases que nos vamos a ocupar son las que están dentro del paquete
De la misma manera para la actualización:
Para los listados, los finders en sí, deberían existir mínimo 3 métodos por cada finder: findBy..(parametros), findBy..(paramétros,start,end), y countBy..(parámetros). Aquí está un ejemplo para el finder
Para lo mismo serán para los demás finders.
La clase completa se podrá ver aquí:
https://github.com/apuntesdejava/liferay-virtual-classroom/blob/master/modules/classroom/classroom-service/src/main/java/com/apuntesdejava/virtualclassroom/service/impl/CourseLocalServiceImpl.java
El servicio remoto, en la clase
Las clases completas se encuentran aquí:
https://github.com/apuntesdejava/liferay-virtual-classroom/tree/master/modules/classroom/classroom-service/src/main/java/com/apuntesdejava/virtualclassroom/service/impl
Tendríamos el siguiente resultado:
Y listo, tenemos nuestros servicios creados y que podemos consumir desde cualquier portlet de Liferay a manera de AJAX. Eso lo veremos en los siguientes posts.
Liferay Service Builder es una herramienta de generación de código que permite, usando un archivo xml, generar una capa completa de servicios. La generación de código incluye definciones de base de datos, código para caché y persistencia, clases de servicios con métodos CURD y la capa de servicios remotos con compatibilidad con JSON y SOAP Web Services.
¡Y todo esto con un solo archivo
service.xml
!En el archivo service.xml podemos definir lo siguiente:
- Información global para los servicios (namespace de la base de datos, paquete de las clases)
- Entidades y atributos (columnas)
- Orden por omisión de la recuperación de los datos
- Métodos de búsqueda de entidades
- Variantes de servicios generados (local y remoto)
- Datasource (interno o externo)
- Excepciones de servicios
- Referencias de servicios disponibles en la generación de clases de servicio.
- Caché a utilizar.
Un ejemplo de qué es lo que tiene el
service.xml
lo podemos ver para la entidade Blogs de Liferay. Aquí podemos encontrar un ejemplo: https://github.com/liferay/liferay-portal/blob/7.1.x/modules/apps/blogs/blogs-service/service.xml
Para continuar con este tutorial, crearemos tres entidades (en el vídeo se muestran dos, y son las que trataremos aquí)
Clic en Finish.
Esto nos creará dos submódulos con el mismo nombre, pero con los subfijos "-api" y "-service".
El módulo classroom-api tendrá solo los API para acceder a los servicios. Hay interfaces, nada de implementación. Es la parte que los demás módulos podrán acceder.
El módulo classroom-service tendrá la implementación en sí. Allí colocaremos la lógica de los CRUD, búsquedas y demás métodos que quisiéramos crear.
y quedaría finalmente así:
- Course
- SessionCourse
- Homework.
Creando el módulo
Para comenzar, necesitamos crear un módulo con la plantilla "service-builder".
Clic Next. Establecemos el paquete donde se elojarán las clases a crear. Colocamos
com.apuntesdejava.virtualclassroom
Clic en Finish.
Esto nos creará dos submódulos con el mismo nombre, pero con los subfijos "-api" y "-service".
El módulo classroom-api tendrá solo los API para acceder a los servicios. Hay interfaces, nada de implementación. Es la parte que los demás módulos podrán acceder.
El módulo classroom-service tendrá la implementación en sí. Allí colocaremos la lógica de los CRUD, búsquedas y demás métodos que quisiéramos crear.
Configurando los módulos
A continuación, necesitamos declarar unas dependencias a cada módulo en sus archivos build.gradle de la siguiente manera.
Para el archivo build.gradle de classroom-api agregar estas dependencias.
compileOnly group: "com.liferay", name: "com.liferay.osgi.util", version: "3.0.0"
Finalmente, quedaría así:
Para el archivo build.gradle de classroom-service agregar estas dependencias.
compileOnly group: "javax.portlet", name: "portlet-api", version: "3.0.0" compileOnly group: "javax.servlet", name: "servlet-api", version: "2.5"
Declarando las entidades
Las entidades se definen en el archivo
Para comenzar, debemos personalizar el namespace, estando en la vista de Overview, seleccionamos el nodo "Service Builder". En el campo "Namespace" colocamos el que deseemos.
Este nombres nos permitirá identificar a estos servicios, además será tomado como un prefijo de las tablas que se crearán en la base de datos.
Luego, en el nodo "Entities" hacemos clic en el botón "Add Entity" y procedemos a crear las entidades.
Y crearemos nuestra primera entidad: Course. Además, aseguremos en activar los checks "Local Service" y "Remote Service"
No tendrá ninguna columna. Vamos a decirle al Service Builder que cree los campos predeterminados para esta entidad, haciendo clic en "Add Default Columns"
Esto nos creará los siguientes campos:
service.xml
que se encuentra dentro del módulo classroom-service. Viene con un ejemplo de Entidad, pero podemos eliminarlo y configurar los nuestros.Para comenzar, debemos personalizar el namespace, estando en la vista de Overview, seleccionamos el nodo "Service Builder". En el campo "Namespace" colocamos el que deseemos.
Este nombres nos permitirá identificar a estos servicios, además será tomado como un prefijo de las tablas que se crearán en la base de datos.
Luego, en el nodo "Entities" hacemos clic en el botón "Add Entity" y procedemos a crear las entidades.
Y crearemos nuestra primera entidad: Course. Además, aseguremos en activar los checks "Local Service" y "Remote Service"
No tendrá ninguna columna. Vamos a decirle al Service Builder que cree los campos predeterminados para esta entidad, haciendo clic en "Add Default Columns"
Esto nos creará los siguientes campos:
courseId
: Es la clave primaria. Está basado en el nombre de la entidad, seguido de id.groupId
: Es el identificador del site de donde se registrarán los cursos.companyId
: Es el identificador de la instancia del portal.userId
: Es el id del usuario que está registrando el curso.userName
: Es el nombre del usuariocreateDate
: Guarda la fecha de creación del cursomodifiedDate
: Guarda la fecha de la última modificación del curso.
Además de estos campos, le agregaremos dos más:
name
Tipo Stringdescription
Tipo String
Solo que name permitirá tener localization. Para ello, después de crearlo, activamos el check Localized en el editor.
También crearemos la entidad CourseSession y tendrá los siguientes campos:
- courseSessionId (long)
- groupId (long)
- companyId (long)
- userId (long)
- userName (String)
- createDate (Date)
- modifiedDate (Date)
- title (String) (Este no tendrá localización)
- description (String)
- dueDate (Date)
- courseId (long).
Finders
Los Finders son métodos de consulta a la base de datos, el cual definimos en la configuración deservice.xml
. Estos hacen caché automáticamente y pueden ser personalizados en cada clase. Para nuestro caso crearemos los siguientes finders por cada entidad:
Entidad | Finder | Columnas |
---|---|---|
Course | UserId | userId |
GroupId | groupId | |
U_G | userId | |
groupId | ||
SessionCourse | CourseId | courseId |
Finalmente, el service.xml debería lucir algo así:
Construyendo las clases
Una vez que tenemos las entidades declaradas, debemos construir las clases. Para ello usaremos las tareas de gradle, y seleccionamos el siguiente:virtual-classroom > modules > classroom > classrom-service > build > buildService
Doble clic, y esperemos a que termine de construir.
Al terminar, presionamos Ctrl+F5 en el árbol del proyecto, y veremos que se crearon clases.
En clases que nos vamos a ocupar son las que están dentro del paquete
com.apuntesdejava.virtualclassroom.service.impl
, porque allí son las implementaciones de los CRUD.Creando los métodos CRUD
Ahora, haremos los métodos para el CRUD de la entidad Course. Todo esto se hace en
CourseLocalServiceImpl
. Todos los -LocalServiceImpl debería tener la implementación directa de la persistencia. Las clases con subfijo -ServiceImpl (que refiere a los servicios remotos) deben acceder a los métodos de los -LocalServiceImple, y no manejar directamente a la persistencia. Además, en estas clases remotas deberían estar el manejo de los permisos.
Entonces, crearemos el primero método de la clase
CourseLocalServiceImpl
public Course addCourse(long groupId, Map<Locale, String> name, String description, ServiceContext serviceContext) throws PortalException { long courseId = counterLocalService.increment(Course.class.getName()); //obtiene un nuevo ID proporcionado por la plataforma Course course = super.createCourse(courseId); //instancia un curso, usando el ID creado arriba course.setGroupId(groupId); //colocamos valores de los parámetros course.setNameMap(name); //el mapa permite guardar por idioma course.setCompanyId(serviceContext.getCompanyId()); //la instancia del portal está en el contexto course.setDescription(description); course.setUserId(serviceContext.getUserId()); User user = userLocalService.getUser(serviceContext.getUserId()); //para obtener... course.setUserName(user.getScreenName()); //... el nombre del usuario course.setCreateDate(serviceContext.getCreateDate(new Date())); //fecha de creacion return super.addCourse(course); //lo guarda en la base de datos y lo devuelve a quien invocó el servicio }
public Course updateCourse(long courseId, Map<Locale, String> name, String description, ServiceContext serviceContext) throws PortalException { Course course = getCourse(courseId); //obtiene el curso para actualizar course.setNameMap(name); //se coloca los nuevos valores course.setDescription(description); course.setModifiedDate(serviceContext.getModifiedDate(new Date())); //se registra la fecha de actualizacion return updateCourse(course); // se invoca a la actualizacion y se devuelve a quién lo invocó. }
Para los listados, los finders en sí, deberían existir mínimo 3 métodos por cada finder: findBy..(parametros), findBy..(paramétros,start,end), y countBy..(parámetros). Aquí está un ejemplo para el finder
UserId
.public List<Course> findByUserId(long userId) { return coursePersistence.findByUserId(userId); } public List<Course> findByUserId(long userId, int start, int end) { return coursePersistence.findByUserId(userId, start, end); } public int countByUserId(long userId) { return coursePersistence.countByUserId(userId); }
Para lo mismo serán para los demás finders.
La clase completa se podrá ver aquí:
https://github.com/apuntesdejava/liferay-virtual-classroom/blob/master/modules/classroom/classroom-service/src/main/java/com/apuntesdejava/virtualclassroom/service/impl/CourseLocalServiceImpl.java
El servicio remoto, en la clase
CourseServiceImpl
, debería tener los mismos métodos, pero invocará a los métodos que están en el servicio local, de la siguiente manerapublic Course addCourse(long groupId, Map<Locale, String> name, String description, ServiceContext serviceContext) throws PortalException { return courseLocalService.addCourse(groupId, name, description, serviceContext); } public Course updateCourse(long courseId, Map<Locale, String> name, String description, ServiceContext serviceContext) throws PortalException { return courseLocalService.updateCourse(courseId, name, description, serviceContext); } public List<Course> findByGroupId(long groupId) { return courseLocalService.findByGroupId(groupId); } public List<Course> findByUserId(long userId) { return courseLocalService.findByUserId(userId); } public List<Course> findByUserId(long userId, int start, int end) { return courseLocalService.findByUserId(userId, start, end); }
Las clases completas se encuentran aquí:
https://github.com/apuntesdejava/liferay-virtual-classroom/tree/master/modules/classroom/classroom-service/src/main/java/com/apuntesdejava/virtualclassroom/service/impl
Desplegando los servicios
Con el liferay en ejecución desde el IDE, arrastremos los dos módulos al servidor de Liferay.
Y listo. En ese mismo momento se crearán las tablas según se han configurado. Para ello podemos revisar la base de datos.
Probando los servicios
Para poder invocar a nuestro servicio necesitamos el valor del groupId, el del site principal. Así que entraremos al panel de control, y en la sección "Settings" del Site por omisión "Liferay" obtenemos el valor. Para mi caso es 20123.
Ahora sí, entremos a la siguiente url: http://localhost:8080/api/jsonws Ahí se muestran todas los servicios remotos publicados.
Seleccionamos el context name con nombre vc que es el namespace que hemos definido al inicio.
En la caja de búsqueda escribimos "add" para buscar el método "add-course":
Y ahí nos mostrarán los parámetros para invocación. Pero en nuestro caso no usaremos esta pantalla de prueba, sino lo haremos desde la consola de javascript que está en Liferay. Así que iremos al inicio del portal http://localhost:8080 y abrimos la consola de javascript de nuestro navegador. Allí escribiremos lo siguiente:
Liferay.Service( '/vc.course/add-course', { groupId: 20123, //nuestro groupId name: {"es_ES":"Nuestro primer curso de Liferay"} , //en formato JSON description: 'Aqui haremos un portlet' //un texto cualquiera }, function(obj) { console.log(obj); //callback para imprimir el resultado } );
Este sería el resultado.
Podemos ver la respuesta obtenida, el valor del companyId, los valores para createDate y userId con userName, el campo "name" con el XML de la localización, etc.
Ahora, también podemos invocar a los otros métodos creados, por ejemplo, podemos consultar el método
Podemos ver la respuesta obtenida, el valor del companyId, los valores para createDate y userId con userName, el campo "name" con el XML de la localización, etc.
Ahora, también podemos invocar a los otros métodos creados, por ejemplo, podemos consultar el método
find-by-group-id
. Su invocación sería así:Liferay.Service( '/vc.course/find-by-group-id', { groupId: 20123 }, function(obj) { console.log(obj); } );
Tendríamos el siguiente resultado:
Y listo, tenemos nuestros servicios creados y que podemos consumir desde cualquier portlet de Liferay a manera de AJAX. Eso lo veremos en los siguientes posts.
Vídeo
Y, por si estaba complicado y queremos verlo en la vida real, aquí el vídeo explicado. Tanto el vídeo como este post se complementan.Código fuente
Y no podía faltar, el código fuente de este proyecto:- Bitbucket: https://bitbucket.org/apuntesdejava/liferay-virtual-classroom/src/service-builder/
- Github: https://github.com/apuntesdejava/liferay-virtual-classroom/tree/service-builder
Comentarios
Publicar un comentario
Si quieres hacer una pregunta más específica, hazla en los foros que tenemos habilitados en Google Groups
Ah! solo se permiten comentarios de usuarios registrados. Si tienes OpenID, bienvenido! Puedes obtener su OpenID, aquí: http://openid.net/