lunes, 31 de diciembre de 2012

Feliz Año Nuevo 2013

Después de años, vuelvo a publicar. No es un post acerca de tecnología.  Es personal y de reflexión acerca del fin de año.

Disfrútenlo.

martes, 1 de diciembre de 2009

Java Day en la UNI

Después de mucho tiempo vuelvo a publicar en mi blog.

El motivo es que he participado por segunda vez en un evento acerca de Java. En esta ocasión fue en el Java Day que organizó la OSUM de la UNI el día 26 de Noviembre.

Realmente me sorprendi bastante cuando mi jefe me consultó si queria participar y cuando el organizador del evento, José Luis Casiano, me invitó directamente. Fue una agradable sorpresa.

En fin, ¿mi tema? Pues lo que más me gusta: la vista de las aplicaciones, en este caso usando Spring 3 MVC. Y es que realmente me estoy orientando hacia esa parte del desarrollo, poco a poco estoy investigando cada vez más acerca del tema y realmente me encantaria llevar el famoso curso de Rich Web Applications with Spring, desde que me lo propusieron este año, he tenido en mente llevarlo. Ojalá se pueda.

Cuando me dijeron cual seria la estructura que más o menos podia seguir para mi exposición, me pareció chevere. Entonces decidí primero construir un modelo de negocio para basarme en el, tanto en la presentación como en la demo que presentaría.

La demo que decidí hacer fue acerca de un restaurante de comida mexicana...y realmente fue una casualidad que coincidera que también fuera de comida (como el que presentamos en el Spring Community Day). Lo digo, porque al inicio pensé en una agenda pero al no hallar ningún template libre (lo admito: desarrollo, pero de diseñadora web tengo poco) que me gustara lo suficiente...lo descarté =P.

Entonces me quedé entre un diario y un restaurant de comida mexicana. El del diario me pareció algo...difícil de acomodar a lo que queria hacer: un maestro simple, un maestro-detalle que use la info del maestro y finalmente un catálogo de imágenes que use la info guardada desde los maestros. Queria darle al público algo relativamente completo. Si no fuera que pudiera enseñarlo todo en la expo, al menos se quedarian con el material para que lo puedan chequear.

Entonces asi fue, hice el modelo de datos y una vez lo tenia me puse a trabajar en la arquitectura que iba a usar. Fue una tarea relativamente sencilla, puesto que habia escogido la versión 3 de Spring, ya tenia la experiencia que me dejó el Spring Community Day y el pasado taller de Spring que habia dictado.

Sin embargo, he de admitir que en esta ocasión hacer todo sola (análisis, diseño, retoque, ajuste a mis necesidades, modelo, dao, servicios, controladores, vista, datos, etc) fue más trabajoso. Mucho más trabajoso. Extrañé mucho al team que se habia formado en el Spring Day, donde cada uno habia hecho lo que más le gustaba y las tareas se habian dividido tan bien que no se sintió tan pesado de presentar.

Pero era todo un reto y a mi me gustan los retos :D. Creo que es una de las cosas que más me motivan: los retos.

En fin, me puse a ello y en una semana, dándole unas horas durante la noche, pude terminar mi demo. Y me gustó como quedó :D. Claro me hubiera encantado hacer más: meterle más Ajax, enriquecer más la vista, etc. Pero bueno, al final el resultado me gusto y mucho.

Ahora hacer la presentación. Hay que ser prácticos y es que realmente armar una presentación no es cosa del otro mundo cuando se trata de contenido. Sabia que queria poner, sabia donde hacer hincapié...lo que no contaba era que unos dias despues mi jefe me dijera que "seria bueno que tú expusieras primero y dieras la intro a Spring" O_O! Jejej bueno en realidad lo pensé pero no tanto. Entonces, me di a ello: una breve introducción.

El contenido ya estaba...ahora solo faltaba algo que la amenizara la cuestión y que gráficamente me apoyara al entendimiento de las ideas. Entonces me puse a revisar algunas diapositivas de mis senseis =^^= y anoté algunas cosas que me llamaron la atención...y segui sus patrones. Asi es como pude agregar algunas imágenes, algunas entre las cuales se encuentra aquella que se no les permitirá nunca olvidar de que trata la inyección dependencias :D.

En fin, terminé de armar la ppt, di un último control de calidad y quedó todo listo.

Cuando llegó el dia, todo fue mejor de lo esperado. Realmente hay que felicitar el grupo OSUM de la UNI: se lucieron. Desde la organización, la recepción, la atención tanto a los asistentes como a los expositores. Y es que realmente se reunió a un grupo de excelentes profesionales:

Diego Limaco - JAVA EE 6
Yo - Spring MVC
José Diaz Diaz - Spring Roo
Juan Carlos Vergara - TDD
Javier Rosado Carrizo - SOA

Se notó perfectamente que la mayoría manejaba su tema.

Al final del evento hubieron algunos sorteos y bueno a los expositores nos secuestraron para agasajarnos con una riquísima cena y un brindis.

Realmente un gran trabajo de parte de los chicos de OSUM y ojalá se repita tanto el evento como la invitación. Un éxito. Gracias por la invitación.

Solo me resta dejarles la presentación que usé



Y la URL de la demo para que la descarguen

https://joedayz.sourcerepo.com/joedayz/susan/demorestaurant usuario invitado pwd invitado

Saludos, y hasta la próxima!

martes, 7 de abril de 2009

Obtener etiquetas de un .properties con JavaScript, DWR y Spring

Hoy por fin retomo la publicación en el blog...¿la razón? Tengo algo interesante que comentar y compartir =).

Tenia asignada una tarea donde tenia que usar una lista dinámica como command de un controller, con Spring, claro.

Ese problema menos mal ya lo habia solucionado con anterioridad. Pero sabia que la parte fuerte estaba en las validaciones y que estas tendrian que ser via JavaScript. ¡Look and Feel!

Bueno eso tampoco era problema...de no ser porque la aplicación maneja muchos idiomas. Aunque por ahora solo nos han pedido Ingles y Español.

¿Y cual es el problema? Pues que los mensajes debian ser estandarizados y mostrados según el idioma que el usuario hubiera escogido desde la primera ventana.

Estamos usando archivos para los mensajes (.properties). Asi que tambien estaban estandarizados los textos de los mensajes.

Entonces...el problema era invocar esas etiquetas desde JavaScript enviando simplemente el key de las mismas...y quizás algún argumento adicional.

Pensando, consultando y leyendo algo...finalmente tome partecitas de algunos consejos, de algunas clases ya implementadas en nuestro framework y un poco de imaginación.

Les presento la solución que le di a este pequeño inconveniente de llamar etiquetas de un archvio de mensajes desde un método JavaScript...usando DWR y Spring =).

Usemos el ejemplo siguiente para mostrar como se podria solucionar la cuestión planteada: deseamos registrar una a una un listado de personas asistentes a un evento. Sin embargo, debemos cuidar que el nombre de la persona siempre sea ingresado.

La solución fue planteada basandome en un método que tiene el SimpleFormController:

[sourcecode language='java']
public String getText(String msgKey, Locale locale) {
return getMessageSourceAccessor().getMessage(msgKey, locale);
}
[/sourcecode]

Entonces la idea para llamar a este método desde algún metodo JavaScript era al menos pasar el Key y el Locale (por el idioma).

Sabia que el Key dependia de lo que quisiera mostrar asi que ello no era problema. Sin embargo el Locale...bueno lo podia conseguir haciendo uso del HttpServletRequest...entonces el foquito empezo a prenderse. Si requeria un objeto HttpServletRequest lo podria obtener a través del Controller.

Entonces, conversando con un compañero de trabajo acerca de ello me sugirió que podria inyectar el request en alguna clase que hiciera un llamado al metodo getText u otro método que hiciera lo mismo. Entonces la idea empezó a tener consistencia:

1. Debia crear una clase a la que pudiera inyectar un objeto HttpServletRequest a través del Controller.
2. Crear un método en la clase mencionada que hiciera lo mismo que el getText del SimpleFormController.
3. Llamar a este método desde otro JavaScript, para lo cual podría usar DWR.
4. Mostrar el mensaje que requiera.

Manos a la obra.

1. class MessagePropertiesLoader: esta clase nos permitirá obtener un objeto HttpServletRequest para obtener el Locale que nos dirá con que idioma se está trabajando en la aplicación. De esta manera al llamarlo desde la vista podremos obtener el mensaje en el idioma correcto.
[sourcecode language='java']
public class MessagePropertiesLoader {

private HttpServletRequest request;
...

public void setRequest(HttpServletRequest request) {
this.request = request;
}
....
}
[/sourcecode]
2. getMessageByResourceBundle: este será el método de MessagePropertiesLoader que será llamado desde la vista y que nos devolverá el mensaje que requrimos.
[sourcecode language='java']
public String getMessageByResourceBundle(String key, String before, String after)
{
String filename = Constants.BUNDLE_KEY;
ResourceBundle bundle = ResourceBundle.getBundle(filename, this.request.getLocale());
String message = bundle.getString(key).trim();

if(ValidationUtil.validateString(before))
{
before = bundle.getString(before).trim();
message = before + " "+message;
}

if(ValidationUtil.validateString(after))
{
after = bundle.getString(after).trim();
message = message + " "+ after;
}
return message;
}
[/sourcecode]
Donde:

[sourcecode language='java']String key [/sourcecode] es el nombre de la etiqueta que deseamos obtener.

[sourcecode language='java']String filename = Constants.BUNDLE_KEY [/sourcecode] nos da el nombre del archivo de propiedades que usamos en la aplicación, en este caso el valor es "resources.properties" para inglés y "resources_es.properties" para español.

[sourcecode language='java']ResourceBundle bundle = ResourceBundle.getBundle(filename, locale) [/sourcecode] nos permitirá acceder al archivo que necesitamos.

[sourcecode language='java']String message = bundle.getString(key).trim() [/sourcecode]una vez abierto el archivo podemos obtener la etiqueta requerida.

Las otras líneas son poder agregar algun parámetro antes o despues del mensaje. Finalmente devolvemos la cadena requerida.

3. Usar la clase implementada: teniendo nuestro controller:
[sourcecode language='java']
public class PeopleFormController extends BaseFormController {

private MessagePropertiesLoader messagePropertiesLoader;
...

protected Object formBackingObject(HttpServletRequest request) throws Exception {

if (!isFormSubmission(request)) {

messagePropertiesLoader.setRequest(request);
People people = new People();
return people;
}
return super.formBackingObject(request);
}
...
}
[/sourcecode]
Donde:
[sourcecode language='java']messagePropertiesLoader.setRequest(request) [/sourcecode]es la sentencia que nos permite acceder al objeto HttpServletRequest.

En nuestro applicationContext.xml:
[sourcecode language='java']


[/sourcecode]

En nuestro action-servlet.xml:
[sourcecode language='java']





[/sourcecode]

Y finalmente para poder acceder a la clase via DWR, en el dwr.xml:
[sourcecode language='java']



[/sourcecode]

4. Solo nos quedaria llamar a la clase y al método desde nuestra vista.
[sourcecode language='html']




Ejemplo Properties, DWR, JS y Spring




Registro de personas asistentes











:
















... ...




[/sourcecode]
Donde:

[sourcecode language='html'] [/sourcecode] nos permitirá acceder a la clase donde estará nuestro método que nos devolverá el valor de la etiqueta requerida.

[sourcecode language='html']input type="button" onclick="addPerson();" value="+"[/sourcecode] es el botón que disparará la acción de agregar una persona a las lista, pero antes validará que se haya ingresado el nombre.

[sourcecode language='html']MessagePropertiesLoader.getMessageByResourceBundle("errors.requiredField", "common.name", "",
function(data) {
alert(data);
}
); [/sourcecode] es el método que finalmente nos arroja?a el mensaje correspondiente al error hayado.

Por último lo que deberian mostrar las etiquetas se encuentran en los archivos .properties:

resources.properties:
[sourcecode language='java']
errors.requiredField= is required.
common.name= Name
common.address=Address
[/sourcecode]

resources_es.properties para español:
[sourcecode language='java']
errors.requiredField= es un campo obligatorio.
common.name= Nombre
common.address=Dirección
[/sourcecode]

Finalmente la aplicación se veria asi para espalñol:

[caption id="attachment_128" align="aligncenter" width="518" caption="Aplicación en Español"]Aplicación en Español[/caption]

Y para el inglés:

dwr_en

Definitivamente esto puede mejorar y se puede llegar a imitar realmente la funcionalidad del getText, que puede recibir argumentos en arreglos de Objetos y más. Pero por ahora me sirve y espero le sirva a alguien más que requiera lo mismo.

Saludos, hasta la próxima.

viernes, 6 de febrero de 2009

Gmail y Twitter: cuando Gmail hace de integrador

Un breve paréntesis de Hibernate para comentar algo que me pasó hoy...

A todo el mundo en algun momento le debe haber sucedido que quiere tener todo en un solo lugar. La idea de centralizar muchas cosas no es nueva, pero creo que como todo en la vida...depende.

El dia de hoy anduve leyendo por ahí algunas cosas y entre mis andanzas me vine a enterar (recién, si recién! u_u) que se puede integrar twitter con Gmail O_o!

Fantástico!, me dije. Y procedí a buscar información al respecto. Encontre la página del juguetito que me permitiria realizar esta integración y halle twittergadget. Yo ya usaba TwitterFox...pero como mencione queria ver la posibilidad de tener mis cuentas más importantes centralizadas en un solo lugar...

Me dirigi a la página de TwitterGadget y me encontré con que explican detalladamente como instalarlo en la cuenta Gmail. Pero tuve un pequeño percance: requeria tener el icono de Labs...pero donde estaba ese icono? Interesante, a buscarlo.

Yo distinguia en el manual que debia de tener el icono que muestro a continuación:

[caption id="attachment_74" align="aligncenter" width="349" caption="Icono de Gmail Labs"]Icono de Gmail Labs[/caption]

Pero a mi por ningún lado me aparecia el dichoso icono. Seguí mi búsqueda. Y encontre el tip para poder acceder a Labs...debia acceder al siguiente link https://mail.google.com/mail/?labs=1#settings desde la página donde esta abierta mi cuenta, así:

[caption id="attachment_113" align="aligncenter" width="414" caption="Dirección de Gmail Labs"]Dirección de Gmail Labs[/caption]

Entonces según decia el manual ya me deberia aparecer el icono de la botellita verde =)...O_O! Nada! No aparecía! T_T...

Seguí buscando...y al fin encontre una explicación...y es que Labs al parecer solo está disponible cuando la cuenta está en inglés...entonces inmediatamente procedí a cambiar el lenguaje de mi cuenta:

[caption id="attachment_79" align="aligncenter" width="564" caption="Cambio de lenguaje de cuenta en Configuración - General"]Cambio de lenguaje de cuenta en Configuración - General[/caption]

Y luego volví a acceder a https://mail.google.com/mail/?labs=1#settings ...y ahora si?! Tarán?! No, no apareció la botellita verde inmediatamente.

No perdí la esperanza e hice lo que recomendaban en otro blog, fui a la opción Settings y ahi lo vi...ahí estaba:

[caption id="attachment_82" align="aligncenter" width="300" caption="Tab de Gmail Labs"]Tab de Gmail Labs[/caption]

Bueno no era le icono que mostraban en el manual pero servia. Una rapida revisión a la cantidad de nuevas funcionalidades que se estan desarrollando para Gmail me dejo bastante sorprendida. Hasta hay una para jugar Snake! *_*! Pero...me dije primero lo primero: twiter.

Localicé la sección de gadgets que debia agregar para poder hacer uso de TwitterGadget. Se trata de la opción "Add any gadget by URL" y se encuentra al final de toda la lista de funcionaliades que ofrece Labs:

[caption id="attachment_91" align="aligncenter" width="300" caption="Opción de agregar Gadgets por URL"]Opción de agregar Gadgets por URL[/caption]

Para activarlo se debe marcar la opción enable que se observa en la imagen dentro del rectangulo rojo, luego se guardan los cambios y voilà! Ahora solo resta configurar TwitterGadget.

En la misma opción Settings de nuestra cuenta, ahora veremos un tab más llamado "Gadgets". Lo elegimos y nos aparecerá una página como la siguiente:

[caption id="attachment_93" align="aligncenter" width="646" caption="Tab Gadget y opción para añadir Gadget por URL"]Tab Gadget y opción para añadir Gadget por URL[/caption]

Lo que hacemos es colocar en la cajita de texto que vemos encerrada en rojo, la url http://www.twittergadget.com/gadget_gmail.xml y darle al botón "Add". Ahora si! Si, ahora si de veras! Voilá!

Podemos regresar a la página de nuestro correo y nos debe aparecer en la parte inferior izquierda lo siguiente:

gmail-integrador-7-post-4

Ingresamos nuestro nombre de usuario, nuestra contraseña y le indicamos cada cuanto tiempo debe buscar nuevos mensajes. Finalmente, si nos identificamos correctamente la vista de nuestro twitter seria la siguiente:

gmail-integrador-8-post-4

Creo que eso seria todo =). Les recomiendo que revisen las demás funcionalidades de Labs, realmente están muy interesantes!

Aunque se debe tomar en cuenta la advertencia que todas estas funcionalidades están en pruebas aún y como bien dice el segundo párrafo del tab de Labs:

If (when) a Labs feature breaks, and you're having trouble getting into your account, there's an escape hatch -- just go to http://mail.google.com/mail/?labs=0 and Labs will be temporarily disabled.


Hay que tomar en cuenta esta información =).

Saludos, hasta la próxima.

viernes, 30 de enero de 2009

Analizando querys…Episodio III: Criteria

Despues de un tiempo de descanso (del blog nada más!) obligado...en favor del Incentives Army...regreso para dar el penúltimo capítulo de la saga de "anécdotas" de Hibernate...saga que ha sido inspirada en el último proyecto terminado en JoeDayz, BSC.

Este episodio será dedicado a una de las mejores herramientas que tiene Hibernate: Criteria...en mi humilde opinión claro está u_u ...pero tambien espero poder hacerlo breve debido a que de este tema existen infinidad de información en la web.

Primero, qué es criteria? Bueno podriamos definirla como una especie de herramienta para realizar consultas a base de datos...una herramienta que difiere bastante de los modos anteriormente vistos (HQL y SQL).

Siguiendo con el mismo ejemplo de los anteriores posts usaremos la tabla DATA_DEMAND y su respectiva clase DataDemand.

En el primer post hablamos de como realizar querys dinámicos haciendo uso de HQL y como tendriamos orientarlos para evitar el engorro de las condicionales a usar. Para ello habiamos usado "1 = 1" para saltarnos no solo el hecho de usar el campo estado como primer filtro, sino tambien para armar fácilmente el query. Así:
[sourcecode language='java']
query.append(" from DataDemand ");
query.append(" where 1 = 1 "); <-------
if(coYear != null && coYear.length() > 0)
{
query.append(" and coYear = " + coYear);
}
if(coMonth != null && coMonth.intValue() > 0)
{
query.append(" and coMonth = " + coMonth);
}
if(coRegion != null && coRegion.intValue() > 0)
{
query.append(" and region.coRegion = " + coRegion);
}
if(coKpi != null && coKpi.length() > 0)
{
query.append(" and kpi.coKpi = '" + coKpi + "' ");
}
[/sourcecode]
Criteria no requiere ninguna especie de truco para hacer ello. Cómo? Así:
[sourcecode language='java']
DetachedCriteria criteria = DetachedCriteria.forClass(DataDemand.class);

if(coYear != null && coYear.length() > 0)
{
criteria.add(Restrictions.eq("coYear", coYear));
}
if(coMonth != null && coMonth.intValue() > 0)
{
criteria.add(Restrictions.eq("coMonth", coMonth));
}
if(coRegion != null && coRegion.intValue() > 0)
{
criteria.add(Restrictions.eq("region.coRegion", coRegion));
}
if(coKpi != null && coKpi.length() > 0)
{
criteria.add(Restrictions.eq("kpi.coKpi", coKpi));
}
criteria.add(Restrictions.eq("stDataDemand", "A"))
.addOrder(Order.desc("coYear"));

List list = getHibernateTemplate().findByCriteria(criteria);
[/sourcecode]
Lo primero que se observa es que se usa el objecto DetachedCriteria, en vez de usar session.createCriteria(DataDemand.class)...esto recuerden que se está desarrollando en una aplicacion con Spring =).

Luego de ello se observa el uso de Restrictions, esta clase nos permite usar operadores de igualdad y demás que se usan en los querys. Algunos de los filtros que le vamos agregando a nuestro query a través del método add de criteria podrian ser:
[sourcecode language='java']
Restrictions.eq(...,...)
Restrictions.in(..., coRegionList), donde coRegionList puede ser cualquier "derivado" de Collections...
Restrictions.between(...)
Restrictions.and(...,...)
Restrictions.isEmpty(...)
Restrictions.isNotEmpty(...)
Restrictions.isNotNull(...)
Etc...
[/sourcecode]
Para el caso del uso de funciones tales como MAX y MIN se debe hacer uso de ProjectionList, por ejemplo:
[sourcecode language='java']
ProjectionList projectionList = Projections.projectionList()
.add(Projections.max("coYear"));
[/sourcecode]
Y luego agregar el projectionList:

[sourcecode language='java'] criteria.setProjection(projectionList);
[/sourcecode]
A primera vista no se ve muy diferente, sin embargo podria decir que el código me parece más limpio, más directo y más elegante =P...hablando a nivel de querys orientados a objectos me refiero.

El nivel y la cantidad de métodos que nos ofrece esta librería es bastante grande. En las pruebas que he realizado ha sido la mejor cuando se trata de armar querys dinámicamente. Querys en los que se requiera mostrar la mayor parte de los atributos de los objecto obtenidos. Esto ya que siempre se requiere tener cuidado en el mapeo de los objectos como vimos en el Episodio II.

El siguiente post por fin espero cerrar esta saga con una breve comparativa entre los tipos de consultas a base de datos antes presentados.

Saludos, hasta la próxima.

sábado, 10 de enero de 2009

Analizando querys…Episodio II: HQL y beans no mapeados

Bueno en este segundo post seguiremos en la linea de HQL directo...para no perder la línea (del tema claro xD) y despues seguir con otra formas (no hql puro) que nos brinde Hibernate para realizar la consultas a base de datos.

El segundo método que quiero analizar es cuando se hace uso de Beans no mapeados. Qué es esto?
Pues se trata de realizar el mapeo directo (sin métodos de asignación intermedia) de consultas hql hacia objetos de clases que no tienen su correspondiente mapping.

En el ejemplo del post anterior teniamos una tabla llamada DATA_DEMAND y su respectiva clase DataDemand (claro sin olvidarnos de su HBM respectivo). También la usaremos para este ejemplo y en los siguientes.

Asumiendo que DataDemand sea una clase que tiene varios atributos de objetos de otros tipos de clases tambien mapeadas, es decir muchas relaciones many-to-one en su HBM...
[sourcecode language='xml']
...









...
[/sourcecode]
Quizás al momento de cargar una lista, el query se ejecute rápidamente. Pero...si las relaciones many-to-one están mapeadas con fetch="select" o fetch="join" pueden pasar dos cosas:

- Tenemos el fetch sin lazy, es decir, tal como se muestra en el ejemplo...aqui si solo accedemos a atributos property (como stDataDemand) no tendriamos problemas. Pero si accedemos a los mapeados como many-to-one (como región), entonces para cada objeto de la lista se realizará un select en tiempo de ejecución para acceder al objeto. Es decir, si la lista de objetos contiene 10 elementos, realizará 10 consultas a base de datos por cada región se acceda. Y si fueran 50? O 200? ^^ Lo mismo ocurre si usamos fetch="join".

- Ahhhhh pues ahora no...lo mapeamos con fetch="select" lazy="false" ^^. Jeje...seguimos analizando y vemos que ahora en vez de realizar el query al momento que accedemos al región ya no realizará el query...porque lo ejecuto al momento de realizar la consulta y trajo ya el objeto región lleno. Pero tenemos otro problema...todos los objetos vienen ya cargados y esto hace demasiado pesada a la lista...Y solo quiero la descripción de región T_T...Emmmm en el caso que hagamos fetch="join" lazy="false" seria lo mismo pero ejecutaria todo en una sola consulta...lógicamente xD.

Entonces...el problema radica en que no requiero todo un objeto (o más objetos) completo...solo requiero una descripción (o solo resultados puntuales)...en mi caso seria un uso incesario de espacio.

Qué hacer?

En mis tantas consultas a mis partners y a la web...halle una solución algo simpatica. Y ahora la comparto con ustedes.

Planteando que es lo que requiero exactamente: un objeto pequeño para mostrar tan solo una serie de descripciones en alguna página...no quiero que me traiga nada más que la descripcion del objeto y la de algunos de los objetos que tiene internamente.

Con HQL directo hariamos?

[sourcecode language='java'] ...
query.append(" select deDataDemand, region.deRegion, kpi.deKpi ");
query.append(" from DataDemand ");
query.append(" where stDataDemand = 'A'");
...[/sourcecode]

De veras asi lo solucionamos? O_o! No lo creo...creo que seria...

[sourcecode language='java'] ...
query.append(" from DataDemand ");
query.append(" where stDataDemand = 'A'");
...[/sourcecode]

Waaaaa...ahora levantamos todos? No, no es necesario toooooooodo . La solución bien podria ser la siguiente (al fin llegue al objetivo del post u_u)...

Definimos un bean con los atributos que requerimos y atención especial al constructor...puede tener otros contructores pero es importante tener uno al menos que inicialice todos los atributos que requerimos:

[sourcecode language='java'] public class BeanDataDemand {

private String deDataDemand;
private String deRegion;
private String deKpi;

public BeanDataDemand(String deDataDemand, String deRegion, String deKpi)
{
this.deDataDemand = deDataDemand;
this.deRegion = deRegion;
this.deKpi = deKpi;
}

...
}
[/sourcecode]

Ahora planteamos el query que mapeará directamente (sin requerir HBM) los resultados de nuestro query hacia nuestro BeanDataDemand:

[sourcecode language='java'] query.append("select new ").append(BeanDataDemand.class.getName());
query.append(" (deDataDemand, region.deRegion, kpi.deKpi) ");
query.append(" from DataDemand ");
query.append(" where stDataDemand = 'A'");

List list = getHibernateTemplate().find(query.toString());
[/sourcecode]

Y ya! La lista list esta conformada por beans del tipo BeanDataDemand y puede ser usada como lo necesitemos. De esta manera podemos usar objetos más pequeños cuando la ocasión lo amerite.

Y bueno desviandome un poco del tema, HQL me refiero...tambien se podria hacer esto mismo con un query...

[sourcecode language='java'] sql.append(" SELECT DD.DE_DATA_DEMAND as \"deDataDemand\", ");
sql.append(" R.DE_REGION as \"deRegion\", ");
sql.append(" K.DE_KPI as \"deKpi\" ");
sql.append(" FROM ");
sql.append(" DATA_DEMAND DD, ");
sql.append(" REGION R, ");
sql.append(" KPI K ");
sql.append(" WHERE ");
sql.append(" DD.CO_REGION = R.CO_REGION ");
sql.append(" AND DD.CO_KPI = K.CO_KPI ");
sql.append(" AND DD.ST_DATA_DEMAND = 'A' ");
return findListOfBeans(sql.toString(), null, BeanDataDemand.class); [/sourcecode]

Todo depende de que se requiera hacer con el resultado que nos arrojará el query. En mi caso yo requeria hacer más ligera la lista de objetos que traia para solo mostrar un listado en una tabla para describir los objetos lo más posible.

En otros casos donde se podrian usar estos tipos de beans son donde se realizan operacion tal como SUM(DD.VALUE) y asi por el estilo...como decia en el párrafo anterior...todo depende de lo que se requiera.

En el siguiente post creo que ya veré el tema de criteria...ojalá sea pronto =D...saludos.

lunes, 5 de enero de 2009

Analizando querys...Episodio I: HQL, el inicio de la saga

Bueno empezaré esto comentando algunas de las soluciones que he ido implementando con respecto a formas de consultar a la base de datos usando como framework de persistencia a Hibernate, compañero (para este ejemplo) de nuestro querido amigo Spring.

La mayoria de esta soluciones han sido planteadas sobretodo para mejorar la perfomance de los querys y...buscando que el usuario deje de susurrarnos al oido "más rápido"...en el buen sentido de la palabra claro está xD.

Para empezar haré algunas comparaciones entre algunos tipos de consultas que devuelvan los mismos resultados y analizaré la performance de cada uno de ellos.

Como bien dice el título primero iré a por el modo HQL, el más simple y el más directo que se puede hacer con Hibernate.

A ver...tenemos una tabla llamada DATA_DEMAND y su respectiva clase DataDemand (cualquier parecido con algun nombre de BSC...no es pura coincidencia =P)...y además un posible query dinámico (con filtros) sería:

[sourcecode language='java']

StringBuffer query = new StringBuffer();
query.append(" from DataDemand ");
query.append(" where stDataDemand = 'A'");

if(coYear != null && coYear.length() > 0)
{
query.append(" and coYear = " + coYear);
}

if(coMonth != null && coMonth.intValue() > 0)
{
query.append(" and coMonth = " + coMonth);
}

if(coRegion != null && coRegion.intValue() > 0)
{
query.append(" and region.coRegion = " + coRegion);
}

if(coKpi != null && coKpi.length() > 0)
{
query.append(" and kpi.coKpi = '" + coKpi + "' ");
}

List list = getHibernateTemplate().find(query.toString());
return list;

[/sourcecode]

Como diria Jack el destripador...ahora vamos por partes ^^...

Como vemos la primera expresión condicional que usamos es "where stDataDemand = 'A'". La mayoria de nosotros usamos el campo estado de las tablas para iniciar los querys (sobretodo cuando tenemos que armarlos dinámicamente)...esto ya que es un campo que siempre estará, que casi siempre será un parámetro por el cual se deba realizar la búsqueda. Entonces asi tendriamos más facil armar del query para usar el prefijo AND en los demás parámetros a usar.

Ahora bien, yendo un poco más alla de esta facilidad para armar querys dinámicos que nos daría usar siempre el campo estado como primer filtro...nos podriamos preguntar cuantos registros de la tabla tendrán este valor? En el caso de la mayoria de aplicaciones que he desarrollado, este campo tiene a dos valores, entonces en el mejor de los casos la mitad de los registros tenian el valor indicado 'A' (aunque en realidad el 90% de los datos tenian el campo estado con valor ACTIVO).

Esto nos lleva a deducir que el query iniciaria levantando la mitad de los registros de la tabla al aplicar el primer filtro de búsqueda...y si la tabla tuviera tan solo ...por ejemplo...1000 registros? O_O! Este tipo de práctica podria llevarnos a iniciar la selección de información con demasiados registros...como 500 registros O_o!

Entonces que hacer? Bueno alguna vez en algun curso en la universidad aprendi la lección (yo tambien hacia esto XD, recordar que en Cato la mayoria de cursos de programación se desarrollan usando JDBC XD). Analizando un poco antes de realizar los querys y con ayuda de mi DBA (y apoyo del JP estrella XD) encontramos que un análisis pequeño antes de colocar el orden en que deberian ejecutarse los filtros de búsqueda nos ayudarian a mejorar la perfomance...aunque sea al inicio de manera imperceptible ^^.

Llegada a esta conclusión...como hariamos con el problema de donde se coloca el AND pues los querys dinámicos pueden empezar con cualquiera de los N filtros que se pasen...pues la solución fue la siguiente:
[sourcecode language='java']

StringBuffer query = new StringBuffer();
query.append(" from DataDemand ");
query.append(" where 1 = 1 ");

if(coYear != null && coYear.length() > 0)
{
query.append(" and coYear = " + coYear);
}

if(coMonth != null && coMonth.intValue() > 0)
{
query.append(" and coMonth = " + coMonth);
}

if(coRegion != null && coRegion.intValue() > 0)
{
query.append(" and region.coRegion = " + coRegion);
}

if(coKpi != null && coKpi.length() > 0)
{
query.append(" and kpi.coKpi = '" + coKpi + "' ");
}

query.append(" and stDataDemand = 'A' ");
List list = getHibernateTemplate().find(query.toString());
[/sourcecode]

Usando la condicional de "1 = 1" (la cual siempre devuelve verdadero) podriamos empezar a armar tranquilamente u,u nuestros querys, ordenando los filtros del modo más conveniente y sin preocuparnos de cual de los filtros es el primero para los siguientes anteponerlos con el AND.

Entonces el query ahora podria haber mejorado en algo su perfomance. Analizándolo muy superficialmente estariamos diciendo "dame los campos que tengan este año, con este mes, de esta región y con este KPI (un campo de la tabla no se asusten XD) y que además tengan estado A (activo)", en vez de "dame los campos tengan estado ACTIVO, con el año indicado, con este mes, de esta region y con este KPI".

Bueno este pequeño truquillo serviria cuando nuestras búsquedas no deberian levantar en primera instancia todos los registros ACTIVOS de una base de datos. Y si este fuera el caso entonces el query al ejecutarse sin filtros quedaria más o menos asi "from DataDemand where 1 = 1 and stKpipDataDemand = 'A' ", lo cual nos devolveria el resultado esperado.

En el episodio 2 analizaremos otra manera de ejecutar el mismo query...quizás usando criteria o beans no mapeados...quizás...quizás...quizás... XD