Icefaces, JBoss, Maven2 y EJB3: Parte 5

0
19963

Icefaces, JBoss, Maven2 y
EJB3: Parte 5.

Vamos ya con la
quinta entrega de este tutorial.

Antes de empezar, os
dejo un enlace a los códigos fuente con lo hecho hasta ahora y
con lo que voy a enseñaros en este:

  1. Objetivo.

    Como
    objetivo en este tutorial es crear, usando icefaces y apoyándonos
    en el proyecto Modelo, una página con esta apariencia:

  • Se mostrará
    una página, donde usando pestañas podremos seleccionar
    ver los libros, los socios o las categorías existentes en la
    base de datos.

  • Los listados se
    mostrarán paginados de 5 en 5.

  • Permitirá
    ordenar por los campos de los registros

  • Existirá
    un campo de texto que filtrará los listados por las letras
    iniciales de los campos de la lista. Este filtrado se refrescará
    automáticamente cada vez que introduzcamos o eliminemos un
    carácter en el campo de búsqueda.

Bueno, pues eso es
básicamente (que no es poco) lo que vamos a hacer.

  1. Cambios
    hechos en el Modelo.

    Primeramente
    os comento una serie de cambios que he tenido que hacer en las
    clases del Modelo para adecuarlas a nuestros requisitos. Los cambios
    han sido los siguientes (si habéis seguido el tutorial
    completo):

  • He
    sacado el fichero ‘orm.xml’ del directorio META-INF (para no
    tenerlo en cuenta, ya que esto se usó a modo de ejemplo), es
    decir, dejamos como modo de mapeo únicamente las anotaciones.

  • He
    modificado todas las entidades donde aparecían relaciones del
    tipo ‘MANY-TO-MANY’ y ‘ONE-TO-MANY’ para que en lugar de
    usar colecciones de tipo ‘Set’, usen colecciones de tipo ‘List’.
    Esto es debido a que en el listado de libros, se usa un componente
    ‘dataTable’ anidado dentro de otro componente ‘dataTable’
    que muestra los libros, para mostrar los autores de un libro (un
    libro puede tener varios autores) y el atributo ‘value’ de este
    componente no convierte automáticamente colecciones de tipo
    Set, pero si de tipo List. Al final he decidido hacerlo en todas las
    relaciones de este tipo para evitar futuros errores, aunque hubiese
    bastado con hacerlo en la relación ‘MANY-TO-MANY’ de la
    entidad Libro.

  • Ha
    sido necesario también, por el mismo motivo que antes,
    modificar la forma de obtener los autores de un libro, indicando que
    NO se obtengan los autores de manera ‘Lazy’ (por defecto) sino
    ‘Eager’, es decir siempre, porque en el ‘dataTable’ anidado,
    cuando intenta obtener los autores de un libro, sino están
    obtenidos previamente, intentará obtenerlos en ese momento
    (‘Lazy’), sin embargo, la sesión del EntityManager ya
    está cerrada y eso provoca un error. El siguiente código
    muestra las dos modificaciones realizadas:


    /**


    *
    Modificamos
    la
    forma
    de
    obtener
    los
    autores
    del
    libro,


    * ya
    que al
    mostrarlos
    en el
    listado
    de
    libros,
    es
    necesario


    *
    obtenerlos
    siempre
    para
    evitar
    un
    error.


    *
    @return


    */

    @ManyToMany(fetch=FetchType.EAGER)

    @JoinTable(name=«Libro_Autor»,

    joinColumns=

    @JoinColumn(name=«libro»,referencedColumnName=«id»),

    inverseJoinColumns=

    @JoinColumn(name=«autor»,referencedColumnName=«id»)


    )

    public
    List<Autor>
    getAutores() {

    return
    autores;

    }

  • He
    modificado la firma de algunos métodos del ‘Dao’, y por
    ende también la implementación ‘DaoImpl’. También
    he añadido una clase llamada ‘FilterParameter’ que
    representa un parámetro de búsqueda en una consulta.
    Os muestro un extracto de algunos métodos del ‘DaoImpl’
    modificados

public
<T
extends
TransferObject> List<T> findAllAndFilterLike(

Class<T>
transferObjectClass, String sortColumn,
boolean
ascending,
boolean
or, FilterParameter … params) {


log.debug(«DaoImpl:findAllAndFilterLike»);


final
String entityName = transferObjectClass.getSimpleName();


StringBuffer sbQuery = new
StringBuffer(
«from
«
).append(entityName).append(»
);


final
Query query;


FilterParameter [] params2
=
null;


boolean
first=
true;


int
i = 0;


if(params!=null
&& params.
length>0)
{

params2
=
new
FilterParameter[params.
length];

for(FilterParameter
fP:params) {


if(first)
{


sbQuery.append(
» where
e.»
).append(fP.getField()).append(»
like :»
).append(«p»+i);


}
else
{


sbQuery.append(or?
» or»:»
and»
).append(»
e.»
).append(fP.getField()).append(»
like :»
).append(«p»+i);


}


first=
false;


FilterParameter fPAux =
new
FilterParameter(
«p»+i,fP.getValue()+«%»);


params2[i] = fPAux;


i++;


}

}

if
(ascending)


sbQuery.append(
» order by e.»
+ sortColumn +
» asc»);

else


sbQuery.append(
» order by e.»
+ sortColumn +
» desc»);

query
= createQuery(sbQuery.toString(), params2);

final
List<T> resultList = query.getResultList();

if
(
log.isTraceEnabled())
{


log.trace(resultList.size()
+
» «
+ entityName +
» recovered
from database.»
);

}

return
resultList;


}

private
Query createQuery(String query, FilterParameter … params) {


Query qQuery =
em.createQuery(query);


if
(params !=
null
&& params.
length>0)
{

for(FilterParameter
fP:params) {


qQuery.setParameter(fP.getField(), fP.getValue());

}

}


return
qQuery;

}

El
primer método permite realizar la búsqueda descrita en
el punto 1. de este tutorial. Recibe una entidad sobre la que buscar
(en realidad la tabla), el campo sobre el que ordenar y la forma de
ordenar el resultado, si la búsqueda es un ‘or’ o ‘and’
sobre los criterios de búsqueda recibidos.

  1. Creamos los
    ManagedBean.

    En
    el proyecto Web, he creado también los ManagedBean sobre la
    que se apoyarán los componentes visuales para ‘pintarse’.
    He creado tres ManagedBean, uno para cada Entidad con la que vamos a
    interactuar (Libros, Categorías y Socios).

    Os
    muestro y comento BookCtrl (los otros dos son similares, podéis
    refactorizar si queréis y sacar lo común a una clase
    abstracta o similar):

package
com.autentia.tutoriales.icefaces.beans;

import
java.util.List;

import
javax.faces.component.UIData;

import
javax.faces.event.ValueChangeEvent;

import
javax.naming.NamingException;

import
org.apache.commons.logging.Log;

import
org.apache.commons.logging.LogFactory;

import
com.autentia.tutoriales.modelo.entidades.Libro;

import
com.autentia.tutoriales.modelo.services.Dao;

import
com.autentia.tutoriales.modelo.services.DaoImpl;

import
com.autentia.tutoriales.modelo.services.FilterParameter;

import
com.autentia.tutoriales.modelo.util.EjbLocator;

/**

*
Managed
Bean
para
los
libros.

*
@author
fjmpaez

*


*/

public
class
BookCtrl {

private
static
final
Log
log
= LogFactory.
getLog(BookCtrl.class);

private
String
sortColumn
=
«titulo»;

private
boolean
sortAscending
=
true;

private
UIData
tabla;

Dao
dao;

private
String
search=«»;

public
BookCtrl() {

log.debug(«Constructor:BookCtrl»);

try
{

dao
= EjbLocator.
lookupLocalBean(DaoImpl.class);

}
catch
(NamingException e) {

log.error
(
«Error al iniciar el
controlador: «
, e);

}

}

public
List<Libro> getAll() {

if(«».equals(search))
{

return
dao.findAll(Libro.class,
getSortColumn(), isSortAscending());

}
else
{

return
dao.findAllAndFilterLike(Libro.class,
sortColumn,
sortAscending,true,

new
FilterParameter(
«titulo»,search),

new
FilterParameter(
«isbn»,search),

new
FilterParameter(
«categoria.nombre»,search));

}

}

public
String getSortColumn() {

return
sortColumn;

}

public
void
setSortColumn(String sortColumn) {

this.sortColumn
= sortColumn;

}

public
boolean
isSortAscending() {

return
sortAscending;

}

public
void
setSortAscending(
boolean
sortAscending) {

this.sortAscending
= sortAscending;

}

public
UIData getTabla() {

return
tabla;

}

public
void
setTabla(UIData tabla) {

this.tabla
= tabla;

}

public
String getSearch() {

return
search;

}

public
void
setSearch(String search) {

this.search
= search;

}

/**


* Este
método
asegura
que
tras
una
búsqueda,
la
tabla
vuelva


* a
mostrar
la
primera
página.


*
@param
event


*/

public
void
changeSearch(ValueChangeEvent event) {

tabla.setFirst(0);

}

}

  • En
    ‘sortColumn’ servirá para almacenar el campo por el que
    se ordenan los listados y ‘sortAscending’ indicará si la
    búsqueda es ascendente o descendente.

  • En
    dao almacenaremos una referencia al ‘Bean’ de sesión
    ‘DaoImpl’ que inicializamos en el constructor del ‘Bean’
    apoyándonos en la clase ‘EjbLocator’

  • El
    atributo ‘search’ lo usaremos para recuperar el texto
    introducido en el campo de búsqueda

  • El
    atributo ‘tabla’ será una referencia al componente JSF
    que muestra los listados. Lo usamos en el método
    ‘changeSearch(…)’ para que cada vez que se produzca un cambio
    en el contenido del campo de búsqueda, la tabla vuelva a la
    página inicial. Si no hacemos esto (o algo similar), cuando
    estemos mostrando una página distinta a la inicial y hagamos
    una búsqueda, provocará que si la búsqueda no
    retorna los suficientes registros como para llegar a mostrar la
    página en la que te encuentras, en la pantalla no aparecerá
    nada sin saber porqué ocurre; por eso forzamos a volver a la
    página inicial.

  • El
    método ‘getAll()’ retornará una lista con los
    registros encontrados. Si ‘search’ está vacío
    devuelve todos los registros de la tabla. Si no lo está,
    pasamos los parámetros correspondientes al método del
    ‘dao’ que filtra.

A
continuación, debemos registrar los ‘Managed Beans’ en el
fichero ‘faces-config.xml’ (muestro sólo el ‘bookCtrl’):



<managed-bean>


<description>

Bean
que maneja los libros


</description>


<managed-bean-name>bookCtrl</managed-bean-name>


<managed-bean-class>

com.autentia.tutoriales.icefaces.beans.BookCtrl


</managed-bean-class>


<managed-bean-scope>session</managed-bean-scope>


</managed-bean>

  1. Creamos la
    parte visual.

    Nos
    vamos a crear una página llamada ‘listBooks.jspx’ (jsp
    sujeto a las restricciones de XML). Cuando integramos icefaces
    podemos seleccionar si queremos que la página ‘corra’ en:

  • Modo
    JSF: Sea procesada por
    javax.faces.webapp.FacesServlet

  • Modo
    icefaces: Sea procesada por
    com.icesoft.faces.webapp.xmlhttp.PersistentFacesServlet

Dependiendo
del ‘mapping’ que usermos al invocarla (podéis mirar el
web.xml para comprobarlo). Si queremos usar componentes icefaces,
debemos usar ‘jspx’ en modo icefaces
(invocarlo con el mapping ‘*.iface)

    Para
    conseguir nuestro objetivo, usaremos los componentes:

  • Para
    las pestañas: ‘<ice:panelTabSet>’ y
    ‘<ice:panelTab>’

  • Para
    el texto de búsqueda: ‘<ice:inputText>’ . Este
    componente realizará un envío parcial o ‘partial
    submit’, que es el ‘truco del almendruco’ de icefaces. Es
    decir, usando AJAX (de manera transparente gracias a Dios), icefaces
    se encargará de enviar la nueva información
    introducida en este campo al modelo de componentes en memoria, y
    refrescando o repintando sólo aquellos componentes que se ven
    afectados por esta nueva información si necesidad de
    refrescar la página completa (Genial ¿ no ?) Por
    defecto, el ‘partial submit’ sólo se realiza (si está
    activado para el componente con el atributo partialSubmit=true)
    cuando el campo pierde el foco (‘onblur’). Yo voy a modificar
    esto para que se realice en el evento ‘onchange’ del campo de
    búsqueda.

  • Para
    pintar los listados y su ordenación: ‘<ice:dataTable>’
    junto con ‘<ice:commandSortHeader>’. Y para la paginación:
    <ice:dataPaginator>

  • Para
    los estilos, usaremos los que ‘regala’ icefaces.

    En
    definitiva, que no tenemos que hacer casi nada, sólo usar
    correctamente los componentes y apoyarnos el los ‘managed beans’
    para obtener datos del modelo..

    Aquí
    va el código de parte de la página resaltando lo más
    importante:

<f:view
xmlns:h=«http://java.sun.com/jsf/html»

xmlns:f=«http://java.sun.com/jsf/core»

xmlns:ice=«http://www.icesoft.com/icefaces/component»

xmlns:jsp=«http://java.sun.com/JSP/Page»>

<html>

<head>

<meta
http-equiv=«Content-Type»
content=«text/html;
charset=iso-8859-1»
></meta>

<title>Panel
de control de la Biblioteca
</title>


<ice:outputStyle
rel=‘stylesheet’
type=‘text/css’
href=‘./xmlhttp/css/xp/xp.css’/>

</head>

<body>


<ice:form
id=«formBiblioteca»>


<ice:panelTabSet>

<ice:panelTab
label=«LIBROS»>

<ice:panelGrid
border=«1»
columns=«2»>

<ice:outputLabel
for=«searchBook»>Campo
empieza por (excepto autor):
</ice:outputLabel>


<ice:inputText
id=«searchBook»
partialSubmit=«false»
value=«#{bookCtrl.search}»


onkeyup=«iceSubmitPartial(form,
this, event); return false;»


valueChangeListener=«#{bookCtrl.changeSearch}»>


</ice:inputText>

</ice:panelGrid>

<ice:panelGrid>

<ice:dataTable
id=«tablaLibros»


var=«book»


value=«#{bookCtrl.all}»


rows=«5»


sortColumn=«#{bookCtrl.sortColumn}»



sortAscending=«#{bookCtrl.sortAscending}»


binding=«#{bookCtrl.tabla}»>


<ice:column
id=«tablaLibroscolumn1»>

<f:facet
name=«header»>


<ice:commandSortHeader


columnName=«titulo»


arrow=«true»>

<ice:outputText
value=«TITULO»></ice:outputText>


</ice:commandSortHeader>

</f:facet>

<ice:outputText
value=«#{book.titulo}»></ice:outputText>


</ice:column>


<ice:column
id=«tablaLibroscolumn2»>

<f:facet
name=«header»>

<ice:commandSortHeader


columnName=«isbn»


arrow=«true»>

<ice:outputText
value=«ISBN»></ice:outputText>

</ice:commandSortHeader>

</f:facet>

<ice:outputText
value=«#{book.isbn}»></ice:outputText>


</ice:column>


<ice:column
id=«tablaLibroscolumn3»>

<f:facet
name=«header»>

<ice:commandSortHeader


columnName=«categoria.nombre»


arrow=«true»>

<ice:outputText
value=«CATEGORIA»></ice:outputText>

</ice:commandSortHeader>

</f:facet>

<ice:outputText
value=«#{book.categoria.nombre}»></ice:outputText>


</ice:column>


<ice:column
id=«tablaLibroscolumn4»>

<f:facet
name=«header»>


<ice:outputText
value=«AUTOR/ES»></ice:outputText>

</f:facet>


<ice:dataTable
id=«tablaLibrostablaAutores»


var=«autor»


value=«#{book.autores}»


rows=«0»>


<ice:column
id=«tablaLibroscolumn11»>

<ice:outputText
value=«#{autor.nombre}»></ice:outputText>


</ice:column>


<ice:column
id=«tablaLibroscolumn12»>

<ice:outputText
value=«#{autor.apellidos}»></ice:outputText>


</ice:column>


</ice:dataTable>


</ice:column>


</ice:dataTable>


<ice:dataPaginator
id=«dataScroll_tablaLibros»


for=«tablaLibros»


paginator=«true»


fastStep=«3»


paginatorMaxPages=«4»>


<f:facet
name=«first»>


<ice:graphicImage


url=«./xmlhttp/css/xp/css-images/arrow-first.gif»


style=«border:none;»


title=«Primera
Page»
/>


</f:facet>


<f:facet
name=«previous»>


<ice:graphicImage


url=«./xmlhttp/css/xp/css-images/arrow-previous.gif»


style=«border:none;»


title=«Anterior
Page»
/>


</f:facet>


<f:facet
name=«next»>


<ice:graphicImage


url=«./xmlhttp/css/xp/css-images/arrow-next.gif»


style=«border:none;»


title=«Siguiente
Page»
/>


</f:facet>


<f:facet
name=«last»>


<ice:graphicImage


url=«./xmlhttp/css/xp/css-images/arrow-last.gif»


style=«border:none;»


title=«Ultima
Page»
/>


</f:facet>


</ice:dataPaginator>

</ice:panelGrid>


</ice:panelTab>





</ice:panelTabSet>

</ice:form>


</body>


</html>

</f:view>

dsdsd

  • Lo
    primero que hemos de poner es ‘<f:view>’ indicando los
    ‘namespaces’ correctamente (diferente a jsp)

  • Luego
    incluimos uno de los estilos por defecto de icefaces con
    ‘<ice:outputStyle>’

  • A
    continuación definimos las pestañas.

  • Luego
    incluimos los listados y recogemos los valores invocando al método
    ‘getAll’ del Bean, le decimos cuales son los atributos
    relacionados con la ordenación, el número de elementos
    por página (un valor 0 no pagina)

  • Incluimos
    el campo de búsqueda. Ponemos el ‘partialSubmit’ a false
    (aunque es el valor por defecto para darle énfasis a la
    explicación) porque lo que queremos es que se envíe la
    información después de pulsar una tecla:
    onkeyup=«iceSubmitPartial(form,
    this, event);…’ .
    Además
    registramos un ‘listener’ para cada vez que cambie el valor y se
    haga una búsqueda, la tabla muestre la página 1.

  • Luego
    anidamos otra tabla para mostrar todos los autores de un libro, esta
    vez sin paginación ni ordenación.

  • Por
    último usamos el componente de paginación indicándole
    que tabla está paginando.


Lo
último que nos queda es modificar la página ‘index.jsp’
para que redireccione a esta nueva página:

<html>

<head>

</head>

<body>

<jsp:forward
page=«listBooks.iface»
/>

</body>

</html>


Usamos
el mapping ‘iface’ para asegurarnos que el servlet que recibe la
petición sea el de icefaces.


Si
ahora arrancamos el servidor e invocamos la aplicación:
http://localhost:8080/Web/
, el resultado obtenido debe coincidir ‘sospechosamente’ con lo
que mostrábamos al principio del tutorial. Os recomiendo que
insertéis más datos en la base de datos para probarlo
mejor.


En
la siguiente entrega intentaré finalizar con toda las
operaciones del CRUD (Create, Read, Update y Delete) de alguna de las
entidades, ya que hasta ahora sólo tenemos operaciones de
lectura.


Hasta
la próxima entrega.

Dejar respuesta

Please enter your comment!
Please enter your name here