Sei sulla pagina 1di 14

Manual para crear una pantalla utilizando Spring DATA, JPA, Spring

MVC, JSP, AngularJS y Bootstrap.


1. Entender el requerimiento
Se necesita una pantalla para administrar las promociones Premium de la plataforma
fungo, desde esta un usuario puede generar una promocin para los usuarios, las promociones
deben de permitir especiar la fecha de inicio y fin, el nmero de promociones y el porcentaje de
descuento, un nombre a la promocin.
De acuerdo al enunciado anterior podemos definir los siguientes requerimientos:
1.1.1.Una pantalla que nos permita ver todas las promociones del punto
1.1.2.Una pantalla donde nos permita dar de alta la programacin de una

2. Spring Data y JPA


Spring data y jpa van de la mano, esta capa tiene dos renglones a continuacin describo cada
una de ellas
2.1.1.Modelo (model) en este paquete van todas las clases que tienen asociada una tabla
(de bases de datos).
2.1.2.Repositorio (repository) en este paquete van todas las interfaces que nos van a
acceder a la informacin de la base de datos (a travs del modelo).
Importante: investigar en internet acerca de JPA y spring data puede ser de mucha ayuda
para tener un contexto ms amplio.

2.2. Modelo
El modelo es una clase en Java que representa mediante anotaciones una tabla en la base
de datos, esto nos ayuda porque l se encarga del mapeo de la clase a la tabla, nosotros siempre
veremos los datos como objetos, es importante que conozcan estos pasos, aunque la mayora de
las veces el encargado de crear las clases en el proyecto es el DBA.
2.2.1.Como primer paso vamos a crear la clase con el nombre de Promocin (notese es en
singular) y con los siguientes atributos: id (este atributo lo llevan todos las clases del
modelo), descripcin, porcentaje, fechaInicio, fechaFin, numeroPromociones,
fechaRegistro y restaurante.

package com.mx.ajal.technology.foodpromotions.model;
import java.io.Serializable;
import java.util.Date;
import
import
import
import
import
import
import
import
import
import
import

javax.persistence.Column;
javax.persistence.Entity;
javax.persistence.FetchType;
javax.persistence.GeneratedValue;
javax.persistence.GenerationType;
javax.persistence.Id;
javax.persistence.JoinColumn;
javax.persistence.ManyToOne;
javax.persistence.Table;
javax.persistence.Temporal;
javax.persistence.TemporalType;

import com.fasterxml.jackson.annotation.JsonIgnore;
@Entity
@Table(name = "PROMOCIONES")
public class Promocion implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID_PROMOCION")
private Integer id;
@Column(name = "DESCRIPCION", length=50)
private String descripcion;
@Column(name = "PORCENTAJE")
private Double porcentaje;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "FE_INICIO")
private Date fechaInicio;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "FE_FIN")
private Date fechaFin;
@Column(name = "NUM_PROMOCIONES")
private Integer numeroPromociones;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "FE_REGISTRO")
private Date fechaRegistro;
@ManyToOne(fetch=FetchType.LAZY)
@JsonIgnore
@JoinColumn(name="RESTAURANTE_ID")
private Restaurante restaurante;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getDescripcion() {
return descripcion;
}
public void setDescripcion(String descripcion) {
this.descripcion = descripcion;
}
public Double getPorcentaje() {
return porcentaje;
}
public void setPorcentaje(Double porcentaje) {

this.porcentaje = porcentaje;
}
public Date getFechaInicio() {
return fechaInicio;
}
public void setFechaInicio(Date fechaInicio) {
this.fechaInicio = fechaInicio;
}
public Date getFechaFin() {
return fechaFin;
}
public void setFechaFin(Date fechaFin) {
this.fechaFin = fechaFin;
}
public Integer getNumeroPromociones() {
return numeroPromociones;
}
public void setNumeroPromociones(Integer numeroPromociones) {
this.numeroPromociones = numeroPromociones;
}
public Date getFechaRegistro() {
return fechaRegistro;
}
public void setFechaRegistro(Date fechaRegistro) {
this.fechaRegistro = fechaRegistro;
}
public static long getSerialversionuid() {
return serialVersionUID;
}
}

Como podemos ver nuestra clase queda con los atributos, anotaciones que lo relacionan con
la base de datos y mtodos get y set.

2.3. Repository.
La capa de persistencia y acceso a los registros de la base de datos se les conoce como
Repository, en algunas ocasiones tambin se les llama DAO (Objetos de Acceso a Datos), para
Spring Data, se utiliza la escritura de persistencia extendiendo de la interfaz genrica
CrudRepository y mandndole como parmetro el nombre del modelo (en este caso
Promocion) y el tipo de dato del identificado (en este caso Integer), entonces nuestra interfaz
genrica quedara de la siguiente manera:
Para crear la interfaz repository tenemos que irnos al paquete de repository crear una nueva
interfaz con el nombre del modelo ms Repository en este caso seria PromocionRepository.
package com.mx.ajal.technology.foodpromotions.repository;
import org.springframework.data.repository.CrudRepository;
import com.mx.ajal.technology.foodpromotions.model.Promocion;
public interface PromocionRepository extends CrudRepository<Promocion,
{
}

Integer>

Si necesitamos alguna consulta a los datos de acuerdo a nuestro requerimiento aqu es


donde lo tenemos que agregar, por default los mtodos que vienen disponible sin escribir nada
ms que lo anterior son los siguientes: save, findAll, exists, etc. Si deseas conocer mas acerca de
estos mtodos por default revisa la siguiente liga: http://docs.spring.io/springdata/commons/docs/current/api/org/springframework/data/repository/CrudRepository.html.
Ahora vamos a modificar nuestra interfaz para realizar un query que nos traiga todas las
promociones de un restaurante, quedando de la siguiente manera:
package com.mx.ajal.technology.foodpromotions.repository;
import java.util.List;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import com.mx.ajal.technology.foodpromotions.model.Promocion;
public interface PromocionRepository extends CrudRepository<Promocion,
{

Integer>

@Query("select p from Promocion p join p.restaurante r where r.id =


:idRestaurante")
public List<Promocion> getPromocionesByRestaurante (@Param
("idRestaurante") Integer idRestaurante);
}

Con esto es hemos terminado todo lo necesario en nuestra capa de persistencia.

3. Servicios
Los servicios tienen como funcin principal, comunicar a los repositorios con los
controladores, es por ello que necesitamos realizar los mtodos necesarios para el funcionamiento
del requerimiento, como vimos anteriormente los requerimientos son los siguientes:

Obtener todas las promociones de un restaurante


Dar de alta una promocin

En los servicios es necesario crear una interfaz y una clase que implemente la interfaz, a la interfaz
la llamaremos PromocionService, y va en el paquete de service y en ella escribimos los dos
mtodos que necesitamos en uno devolvemos una lista de promociones y en la otra los mensajes,
quedando la interfaz de la siguiente manera:

package com.mx.ajal.technology.foodpromotions.service;
import java.util.List;
import com.mx.ajal.technology.foodpromotions.dto.Mensaje;
import com.mx.ajal.technology.foodpromotions.model.Promocion;
import com.mx.ajal.technology.foodpromotions.model.Restaurante;
public interface PromocionService {
public Mensaje guardarPromocion(Promocion promocion, Restaurante
restaurante );
public List<Promocion> getPromocionesByRestaurante(Restaurante
restaurante);
}

Ahora vamos a crear la implementacin esta se debe de llamar como el servicio mas Impl en este caso quedara
PromocionServiceImpl.
package com.mx.ajal.technology.foodpromotions.service.impl;
import java.util.Date;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import
import
import
import
import

com.mx.ajal.technology.foodpromotions.dto.Mensaje;
com.mx.ajal.technology.foodpromotions.model.Promocion;
com.mx.ajal.technology.foodpromotions.model.Restaurante;
com.mx.ajal.technology.foodpromotions.repository.PromocionRepository;
com.mx.ajal.technology.foodpromotions.service.PromocionService;

@Service
public class PromocionServiceImpl extends BaseService implements
PromocionService{
@Autowired PromocionRepository promocionRepository; // inyectamos el
repositorio
/**
* Este metodo trata de guardar una promocion y devuelve un mensaje,
* con el valor 0 si todo pasa correctamente y en caso contrario manda un
id
* diferente a 0
*/
public Mensaje guardarPromocion(Promocion promocion, Restaurante
restaurante) {
Mensaje mensaje = new Mensaje();
try{
promocion.setFechaRegistro(new Date()); // establecemos la
fecha actual
promocion.setRestaurante(restaurante); // le seteamos el
restaurante correspondiente
promocionRepository.save(promocion); // guardamos la
informacion
}catch(Exception e){
LOG.error(e.hashCode()+":"+e.getMessage(),e);
mensaje.setId(e.hashCode());
mensaje.setMensaje(e.getMessage());
}

return mensaje;
}
public List<Promocion> getPromocionesByRestaurante(Restaurante restaurante)
{
return
promocionRepository.getPromocionesByRestaurante(restaurante.getId()); //
devolvemos las promociones del restaurante.
}
}

4. Controladores
Los controladores son los encargados de comunicar a una pantalla con la capa de servicios, es
importante que un controlador no invoque directamente a un repositorio sino que siempre
pase por la capa de seguridad, aqu vamos a identificar los controladores sncronos y
asncronos
Sincronos, es cuando se hace una peticin al servidor, como por ejemplo cambiar de pantalla,
de estos vamos a tener solamente uno que se llame verPromociones dentro del controlador
de restaurates y que nos va a devolver un String que nos regrese la url del jsp a mostrar
Agregamos el mtodo a la clase RestauranteController, ya que esta ya exista solo le
agregamos un nuevo mtodo
@RequestMapping(value="ver-promociones.html")
public String verPromocion(HttpServletRequest request){
if (getLicenciaFromSession(request)==null){ // si no tiene
una sesion lo regresamos a inicio.html
return "redirec:/app/restaurante/inicio.html";
}
return "restaurante/ver-promociones";
}

El mtodo lo que hace es regresar el string de restaurante/ver-promociones este archivo lo


tenemos que crear sino existe, como pueden ver antes de regresar esto preguntamos si el usuario
tiene una licencia valida sino lo regresamos a inicio esto es porque solamente podrn usar esta
promocin los usuarios con una licencia activa.

Asincronos, los mtodos de los controladores asncronos nos sirven para hacer peticiones
al servidor sin neceidad de refrescar, sino que lo hacen por abajo a esto se le llama tambin
peticiones AJAX, para la pantalla que estamos desarrollando necesitamos dos mtodos asncronos
uno que nos permita guardar y otro que nos permita obtener todos las promociones del
restaurante.
Como en este caso no existe una clase asncrona para las promociones la vamos a crear en
el paquete de async.controller y se llamara AsyncPromocionController, en ella pondremos los dos
mtodos que necesitamos para guardar y para obtener las promociones de un restaurante.

El controlador asncrono queda de la siguiente manera:


package com.mx.ajal.technology.foodpromotions.async.controller;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import
import
import
import
import
import

org.springframework.beans.factory.annotation.Autowired;
org.springframework.stereotype.Controller;
org.springframework.web.bind.annotation.RequestBody;
org.springframework.web.bind.annotation.RequestMapping;
org.springframework.web.bind.annotation.RequestMethod;
org.springframework.web.bind.annotation.ResponseBody;

import
import
import
import

com.mx.ajal.technology.foodpromotions.controller.BaseController;
com.mx.ajal.technology.foodpromotions.dto.Mensaje;
com.mx.ajal.technology.foodpromotions.model.Promocion;
com.mx.ajal.technology.foodpromotions.service.PromocionService;

@Controller
@RequestMapping("restaurante/promocion/async")
public class AsyncPromocionController extends BaseController {
@Autowired PromocionService promocionService;
@RequestMapping(value="guardar-promocion",
method=RequestMethod.POST)
public @ResponseBody Mensaje guardarPromocion(@RequestBody
Promocion promocion, HttpServletRequest request){
return promocionService.guardarPromocion(promocion,
getRestauranteFromSession(request));
}
@RequestMapping("ver-promociones")
public @ResponseBody List<Promocion>
getPromocionesByRestaurante(HttpServletRequest request){
return
promocionService.getPromocionesByRestaurante(getRestauranteFromSession(re
quest));
}
}

Lo que estamos haciendo aqu es exponer en rutas de http para que puedan ser accedidas dentro
de Ajax.

5. JSP
A continuacin vamos a modificar la jsp restaurante/inicio.jsp para agregar un botn siempre que
exista una licencia valida, lo nico que va hacer es mandarnos a la pantalla ver-promociones.html
que fue agregada al controlador RestauranteController, si recordamos este mtodo nos devuelve
restaurante/ver-promociones, el cdigo que agregamos en el JSP es el siguiente:
<c:if test="${licenciaSession!=null}">
<form action="ver-promociones.html">
<button class="btn btn-primary">Administrar</button>
</form>
</c:if>

Como vemos el mtodo verPromociones de la clase RestauranteController nos regresa un String


con restaurante/ver-promociones es importante que nos fijemos en algo, todas nuestras
pantallas que se ven estn dentro de la carpeta WEB-INF/views/, entonces tenemos que crear
dentro de la carpeta restaurante (WEB-INF/views/restaurante/) un archivo .jsp con el nombrer de
ver-promociones.jsp, el cual en un principio lo dejaremos con el siguiente aspecto.

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>


<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<!DOCTYPE html>
<html lang="en" ng-app="restauranteApp">
<head>
<title>FP Restaurante</title>
<jsp:include page="/WEB-INF/views/commons/head.jsp"/>
</head>
<body ng-controller="restauranteCtrl">
<c:set var="restaurante" value="${restSession}"></c:set>
<jsp:include page="/WEB-INF/views/commons/header.jsp"/>
<div class="container">
<h2>Promociones</h2>
</div>
<jsp:include page="/WEB-INF/views/commons/footer.jsp"/>
<script
src="${pageContext.servletContext.contextPath}/resources/js/rest/verpromociones.js"></script>
</body>
</html>

Y vamos a crear un archivo js (javascript) en la ruta /resources/js/rest/ver-promociones.js con el


siguiente aspecto:
var URL_BASE_PROMOCION = BASE_APP+'app/restaurante/promocion/async/';
app.controller("restauranteCtrl", function($scope, $http) {
});

Vamos a probar lo que hemos hecho hasta ahora.

Ya nos aparece el botn Administrar promociones y si lo presionamos:

Ya nos lleva primero vamos hacer una tabla en la cual mostremos todas las promociones y si no
existe un mensaje que nos diga que no ha dado de alta ninguna promocin.

<table class="table table-bordered table-hover">


<thead>
<tr>
<th>Descripcion</th>

<th>Porcentaje</th>
<th>Fecha Inicio</th>
<th>Fecha Fin</th>
<th>Numero de Promociones</th>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-hide="promociones.length">
<td colspan="6">No tienes promociones dadas
de alta.</td>
</tr>
<tr ng-repeat="p in promociones">
<td>{{p.descripcion}}</td>
<td>{{p.porcentaje}}</td>
<td>{{p.fechaInicio}}</td>
<td>{{p.fechaFin}}</td>
<td>{{p.numeroPromociones}}</td>
<td></td>
</tr>
</tbody>
</table>

Ahora vamos a declarar una variable en el scope de nuestro java script que se llame promociones y
la vamos a inicializar vacia, nuestro js quedara de la siguiente manera:
var URL_BASE_PROMOCION = BASE_APP+'app/restaurante/promocion/async/';
app.controller("restauranteCtrl", function($scope, $http) {
$scope.promociones = [];
});

Ahora vamos a crear un mtodo que nos regrese todas las promociones de ese restaurante es aqu
donde vamos a invocar a un metodo asncrono a continuacin como quedara:
var URL_BASE_PROMOCION = BASE_APP+'app/restaurante/promocion/async/';
var URL_BASE_PROMOCION_VER = BASE_APP+'ver-promociones';
app.controller("restauranteCtrl", function($scope, $http) {
$scope.promociones = [];
$scope.refreshPromociones = function (){
$http.get(URL_BASE_PROMOCION_VER)
.then(function(response) {
$scope.promociones = response.data;
});
};
});

Y refrescamos nuestra pantalla para ver si se refleja algn cambio con lo anterior:

Ahora vamos a crear un formulario para el alta de una promocin, queda de la siguiente manera:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<!DOCTYPE html>
<html lang="en" ng-app="restauranteApp">
<head>
<title>FP Restaurante</title>
<jsp:include page="/WEB-INF/views/commons/head.jsp"/>
</head>
<body ng-controller="restauranteCtrl">
<c:set var="restaurante" value="${restSession}"></c:set>
<jsp:include page="/WEB-INF/views/commons/header.jsp"/>
<div class="container">
<h2>Promociones</h2>
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>Descripcion</th>
<th>Porcentaje</th>
<th>Fecha Inicio</th>
<th>Fecha Fin</th>
<th>Numero de Promociones</th>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-hide="promociones.length">
<td colspan="6">No tienes promociones dadas
de alta.</td>
</tr>
<tr ng-repeat="p in promociones">
<td>{{p.descripcion}}</td>
<td>{{p.porcentaje}}</td>
<td>{{p.fechaInicio}}</td>
<td>{{p.fechaFin}}</td>

<td>{{p.numeroPromociones}}</td>
<td></td>
</tr>
</tbody>
</table>
<div class="panel panel-info">
<div class="panel-heading">Alta de una promocin</div>
<div class="panel-body">
<form name="formAltaPromocion" action=""
class="form form-horizontal">
<div class="form-group">
<label class="col-md-4 controllabel">Descripcin</label>
<div class="col-md-8">
<input type="text" class="formcontrol" required="required" maxlength="30" name="descripcion" ngmodel="promocion.descripcion" />
</div>
</div>
<div class="form-group">
<label class="col-md-4 controllabel">Porcentaje</label>
<div class="col-md-8">
<input type="number"
class="form-control" required="required" maxlength="2" name="porcentaje"
ng-model="promocion.porcentaje" />
</div>
</div>
<div class="form-group">
<label class="col-md-4 controllabel">Fecha Inicio</label>
<div class="col-md-8">
<input type="date" class="formcontrol" required="required" name="finicio" ngmodel="promocion.fechaInicio" />
</div>
</div>
<div class="form-group">
<label class="col-md-4 controllabel">Fecha Final</label>
<div class="col-md-8">
<input type="date" class="formcontrol" required="required" name="ffinal" ng-model="promocion.fechaFin"
/>
</div>
</div>
<div class="form-group">
<label class="col-md-4 controllabel">Numero de promociones</label>
<div class="col-md-8">
<input type="number"
class="form-control" required="required" maxlength="2" name="promociones"
ng-model="promocion.numeroPromociones" />
</div>
</div>
<div class="form-group">
<div class="col-md-8 col-md-offset-4">

<div class="alert alert-warning


alert-immer-resume" ng-show="formAltaPromocion.$invalid">
<strong>Favor de validar la informacin
proporcionada</strong>
<ul>
<li ng-repeat="(key, errors) in
formAltaPromocion.$error track by $index">
<ul>
<li ng-repeat="e in errors">{{
e.$name }} tiene el error: <strong>{{ key }}</strong>.</li>
</ul>
</li>
</ul>
</div>
<button type="button" ngclick="guardarPromocion()" ng-disabled="!formAltaPromocion.$valid"
class="btn btn-primary">Guardar</button>
</div>
</div>
</form>
</div>
</div>
</div>
<jsp:include page="/WEB-INF/views/commons/footer.jsp"/>
<script
src="${pageContext.servletContext.contextPath}/resources/js/rest/verpromociones.js"></script>
</body>
</html>

Y el javascrcipt:
var URL_BASE_PROMOCION = BASE_APP+'app/restaurante/promocion/async/';
var URL_BASE_PROMOCION_VER = URL_BASE_PROMOCION+'ver-promociones';
var URL_BASE_PROMOCION_GUARDAR = URL_BASE_PROMOCION+'guardar-promocion';
app.controller("restauranteCtrl", function($scope, $http) {
$scope.promociones = [];
$scope.promocion = {};
$scope.refreshPromociones = function (){
$http.get(URL_BASE_PROMOCION_VER)
.then(function(response) {
$scope.promociones = response.data;
});
};
$scope.guardarPromocion = function (){
$http({
method:'post',
url: URL_BASE_PROMOCION_GUARDAR,
data: $scope.promocion
}).then(function (res){
$scope.refreshPromociones();
$scope.promociones = [];
$scope.promocion = {};

showMessageServ(res.data);
});
};
$scope.refreshPromociones();
});

Potrebbero piacerti anche