sábado, 17 de octubre de 2009

Warp Persist y Hibernate

He comenzado a jugar un poco con Google Guice, un contenedor liviano de inyección de dependencias y no pude evitar preguntarme como integrarlo con hibernate... la respuesta es AOP, y a través de warp persist es aun mas facil realizar el soporte transaccional.
En estos dias voy a subir un ejemplo a code.google para poder bajarlo facilmente.

miércoles, 22 de julio de 2009

Problemas con el Debugger en Flex Builder 3

Al intentar debuggear una aplicación en Flex Builder 3, me salió un cartel de error diciendo que no se encontraba el Debugger, y una pregunta "desea debuggear de todas formas?"; si ponemos SI o NO... da igual, No pasa nada.
La solución fue ir a C:\Program Files\Adobe\Flex Builder 3\Player\10\win e instalar el archivo Install Flash Player 10 ActiveX.exe.
En la misma sesión y sin cerrar ni resetear el Flex Builder pude realizar el debug sin problemas.
Nota: les va a dar un error si tienen algún navegador abierto, lo cierran y continúan con la instalación sin problemas

sábado, 21 de febrero de 2009

Query Level Cache en Hibernate

En ciertos casos, nos interesa cachear el resultado exacto de una consulta, no objetos individuales. Esto sería util cuando realizemos consultas cuyo resultado varíe muy poco, entonces es recomendable establecer la consulta como "cacheable".

Hay que tener en cuenta que el query cache solo almacena los id's de los objetos del resultado, es decir que lo debemos usar combinado con el Second Level Cache.

Es recomendable usar el query cache solo en los casos en que una consulta se repite constantemente y la entidad resultado no cambia frecuentemente, por ejemplo: Países, Estados, Tipos y Monedas.

Su uso representa otra opción para mejorar la performance y rendimiento de nuestras aplicaciones.

Supongamos la consulta:

List monedas = sess.createQuery("from Moneda")
.setCacheable(true) <--
Aqui establecemos la consulta como cacheable
.list();
La primera vez que se ejecuta la consulta, retornará la lista de Monedas desde la tabla mediante un select, pero a partir de ese momento toda vez que se repita el query, el resultado va a ser retornado desde el cache, evitando la comunicación con la base de datos.

¿Que pasa si se agrega una nueva Moneda?
Si agregamos una nueva Moneda pasando por la session de Hibernate, el query se invalida automáticamente para que la próxima vez que se ejecute la consulta vuelva a obtener el resultado desde la base de datos.
Si agregamos una nueva Moneda por fuera de ESA session, el query no se invalida; sería lo mismo que hacer un insert directo a la tabla, entonces habría que usar JNDI para que todos compartan la misma SessionFactory.

Entonces para el ejemplo que venimos manejando, hay que realizar los siguientes pasos:
  1. Establecer el property hibernate.cache.use_query_cache=true en el hibernate.cfg.xml
  2. Configurar la entidad Moneda como cacheable

Hibernate caching basics

En Hibernate hay dos tipos de cache: el First Level y el Second Level.
  • First Level Cache (asociada con el objeto Session)
    • Mantenido auntomaticamente por Hibernate cuando dentro de una Transaction interactuamos con la base de datos, en éste caso se mantienen en memoria los objetos que fueron cargados y si mas adelante en el flujo del proceso volvemos a necesitarlos van a ser retornados desde el cache, ahorrando accesos sobre la base de datos. Esto implica que si un objeto es modificado muchas veces dentro de la misma Transaction, Hibernate generará solamente un SQL UPDATE al final de la Transaction.
    • Es válido solamente entre el begin() y el commit() de una Transaction, en forma aislada a las demás.
    • Hibernate lo maneja por defecto, no hay que configurar nada, si por alguna razón queremos deshabilitar o evitar el uso del cache, podemos usar un tipo especial de session: StatelessSession, se obtiene de la sessionFactory con el método openStatelessSession(). Es usada para procesos batch, por ejemplo si hay que hacer inserts o updates masivos, evitando que cada vez que se hace el save de un objeto, el mismo me quede en memoria y en el correr del proceso se produzca un error del tipo OutOfMemoryError. La StatelessSession no interactúa con el First Level Cache ni con el Second Level Cache, es casi como si utilizáramos JDBC directamente.
  • Second Level Cache (asociada con el objeto SessionFactory)
    • Se utiliza para mejora de la performance.
    • Es válido para todas las transacciones. Mantiene objetos cargados a un nivel de SessionFactory mas allá de la duración de las muchas Transaction que podamos tener.
    • Los objetos en la L2 cache están disponibles durante toda la aplicación. Lo podríamos considerar como un cache global.

Cuando y donde va el inverse=true?

Supongamos una relacion 1..* bidireccional Person->Address, es decir que una persona tiene uno a N direcciones.
Esto implica que Person es el padre, y Address el hijo.

En el mundo de los objetos esto sería : la clase Person que tiene un método "Set
getAddresses()" y la clase Address que tiene un método "Person getPerson()".

Si lo pensamos en el mundo relacional Person sería PERSON[ id, name, ...] y Address sería ADDRESS[ id, person_id, city, street,...].
O sea que Address es el dueño de la relación... y Person es el lado inverso.
Es decir que "inverse=true" significa "este es el lado inverso" y "inverse=false" significa "este no es el lado inverso, es el dueño de la relación".

Otra forma de ver esto podría ser volviendo a PERSON[ id, name, ...] y ADDRESS[ id, person_id, city, street,...], en la que se observa que la columna person_id de la tabla Address es la informacion relacional entre las dos tablas. O sea que Address es el dueño de la relación, y Person es el lado inverso, como se dijo antes.

viernes, 20 de febrero de 2009

Usar SQLQuery de HIbernate cuidadosamente

Hibernate provee dos API's de consultas de manera nativa a la base de datos, HQL y Criteria.
Pero también podemos hacer consultas en SQL nativo, pensado en funcionalidades muy particulares del RDBMS en el que estamos trabajando y que HQL o Criteria no nos provee en ese cierto contexto.

Esto puede ser tentador cuando uno da sus primeros pasos con este ORM, pues no habría que dedicar tiempo a aprender estos lenguajes y además podríamos probar nuestras consultas en alguna herramienta gráfica como DBVisualizer si fuera necesario, además de que cualquiera que no sea familiar a HQL o Criteria podría entender facilmente nuestra SQLQuery en plain SQL.

Pero hay algunas razones para evitar su abuso.

La SQLQuery de Hibernate bypassea la Cache de Session y realiza la consulta contra la base de datos.
Esto significa que si realizamos una consulta SQL en la misma transacción que hicimos un save o saveOrUpdate, los objetos grabados o actualizados en la Cache de Session no serán incluidos en el resultado de la consulta SQL.

La diferencia con querys en HQL o Criteria, es que éstas chequean las Cache de Session antes de ejecutar la consulta. Si hay objetos que ejecutar contra la base de datos, Hibernate hará automaticamente un session.flush() de la cache.
Esto implica que a diferencia de SQLQuery, HQL y Criteria incluirán los objetos en la Cache de Session el 100% de las veces.

Ahora bien, uno podría sugerir forzar el session.flush() antes de la SQLQuery y asunto arreglado... pero el problema es que el session.flush() es una operación costosa y HQL y Criteria (como tienen todos los objetos en la Cache de Session) simplemente pueden decidir cuando es necesario realizarlo.

Por qué Hibernate no crea bases de datos?

Una frase que me dijeron una vez y que siempre la tengo presente es "Lo único que Hibernate no va a hacer por vos, es crear la base de datos, todo lo demás lo podés hacer programaticamente". Para mi es casi poesía.
Y he visto gente que espera que Hibernate haga esto.
Alguien me sugirió que la respuesta a esta pregunta era simplemente "Por que Hibernate NO hace eso".
Tratemos de explicarlo al menos...
Primeramente, por que Hibernate es un ORM que esta pensado para trabajar con muchos RDBMS, por ende...como puedo decirle programaticamente cual es el motor que voy a usar? que yo sepa no se puede.
Segundo, Hibernate toma un hibernate.cfg.xml como punto de partida para hacer su trabajo (llevar mis objetos al mundo de las tablas) que si se fijan bien es ahí donde le digo con que rdbms, base de datos, usuario y contraseña se tiene que conectar... a una base de datos ya creada!