La carga perezosa: hibernate vs jdo

Tengo que mirar más en detalle JPA (Java Persistence Api), pero si los propios miembros de la especificación lo suponen un subconjunto de JDO (Java Data Object), es de suponer que sea parecido. De todos es conocido el problema de Hibernate (y cualquier ORM) con lazy loading, de hecho ya hemos hablado aquí sobre ello. Básicamente sucede cuando después de cerrar el la transacción en el controlador (una acción de struts por ejemplo, o una clase de servicio) queremos cargar objetos relacionados definidos como lazy en la vista. Ahí aparece el temido LazyInitializationException.


Hoy no voy a hablar de eso, ya hay soluciones como el OpenSessionInView (he olvidado el término patrón a propósito), o más inteligentes, como utilizar Spring, por mucho que a alguno le pese. Pero en lugar de eso, voy a hablar de un desconocido, y como lo resuelve él: JDO, por si alguien tiene que escoger un ORM y se niega al borreguismo (dicho esto sin acritud hacía Hibernate, sino simplemente quiere ver distintas opciones, que nunca es malo).


En JDO, al igual que la mayoría de los ORM, tenemos por una parte nuestros beans, por otra la base de datos, y en medio tenemos un fichero con metainformación que relaciona ambas cosas, normalmente un xml. Sin entrar en detalles de como se trabaja con JDO, casi todos podrán entender:

        
            
            
                
            
            
                
            
            
                
                
            
            
                
                
            
            

            
            
                

                
                
            

        



Esta es la definición de una clase Task, que tiene varias relaciones (los campos primitivos no hace falta definirlos si nos valen los valores por defecto). La parte interesante es la del final, la llamada fetch-group.


Y es que por defecto se cargan las relaciones de forma perezosa, como en casi todas partes (y por tanto tendríamos el mismo problema). En JDO podemos definir distintos fetch-groups, para distintos usos normales de la clase, definiendo que queremos que se cargue en esos casos. Por ejemplo, en este caso, cuando cargamos un grupo de Task para un listado, se cargará el grupo por defecto (primitivos + String + Date, pero no relaciones, salvo status porque así lo hemos definido), pero cuando queremos mostrar una página de detalle, nos interesa tener a mano información sobre el tipo de tarea que es (taskType), su estado (en este caso es un objeto, no un valor, status), o si tiene añadidos varios fallos (bugs). Dejamos sin cargar los partes de horas, historial de cambios, etc.

Simplemente a la hora de hacer la consulta haremos algo así como (escribo de memoria):

query.getFetchPlan().addFetchGroup("detailsPage");



Limpíto, ¿no?.

6 Respuestas a “La carga perezosa: hibernate vs jdo”


  1. 1 gimenete

    Hibernate soporta “lazy loading”, pero también soporta “eager fetching”. Ejemplo:

    List empleados = session.createCriteria(Empleado.class).setFetchMode(?departamento?, FetchMode.EAGER).list();

    eso carga los “empleados” de la base de datos y también su “departamento” relacionado. así al hacer empleado.getDepartamento().getNombre() no se requiere hacer una consulta en la base de datos. Haciendo

    List empleados = session.createCriteria(Empleado.class).list();

    obtenemos la lista de empleados, pero si hacemos empleado.getDepartamento().getNombre() si entra en juego el lazy loading. vamos, que se pueden hacer las dos cosas.

    un saludo!

  2. 2 Al

    Hola. Supongo que me he explicado mal, no quería decir que no hubiera más opciones dentro de hibernate, simplemente la de JDO me parece elegantísima para agrupar registros y relaciones para distintos usos.

  3. 3 Monteagudo

    Buenas,

    a sabiendas de que lo que voy a comentar no es lo que Alberto pretende aclarar en este post me gustaría matrizar que, a no ser que haya algo que me haya perdido, creo que integrando Spring con Hibernate no te libera de tener que utilizar el “patrón” OpenSessionInView.

    Al margen de esto, la solución que personalmente más me gusta hasta ahora es la utilización de SessionBeans con Estado con la nueva especificación EJB3. Al utilizar un EntityManager extendido dentro de un SFSB, la conexión permanece abierta mientras existe el SFSB, y nunca te tienes que preocupar de que al hacer el acceso a listas cargadas de forma perezosa éstas vayan a causar problemas.

    Una solución mejorada bajo mi punto de vista es la que utiliza Seam, que puede delimitar el ciclo de vida del SFSB dentro de unos contextos mejor definidos, especialmente el contexto “conversation”.

    Saludos,

    José Luís Monteagudo

  4. 4 alfredo casado

    Tengo pendiente mirar JDO… a ver si encuentro tiempo.

    Siguiendo la idea de gimenete pero cambiado criteria por HQL y named querys puedo obtener un solución muy similar con hibernate.

    En el mapeo de la entidad puedo definir “named querys” utilizando “join fetch”, Luego en mis DAO’s implemento los métodos que ejecutan estas querys y si quiero de nombre les pongo “FetchPlanblabla” y tengo algo realmente muy parecido, menos elegante y más “manual” pero con basicamente las mismas ventajas y también con los mismos incovenientes.

    Con estos fetch plans hay dos cosas que siguen sin cuadrarme del todo:

    - No puedo definir un fetch plan que cargue dos colecciones para no producir un producto cartesiano, por tanto si tengo más de una colección tendre colecciones que se cargarán “lazy” de todos modos. (esto al menos es cierto para el eager fetching de hibernate, en JDO quizas no sea un problema)

    - Mis objetos siguen teniendo métodos que pueden cascar en la vista. Si alguien hace el fetch plan pero el encargado de hacer la vista no conoce los detalles puede invocar a métodos que produzcan “lazy exceptions” de todos modos. O se documentan bien los fetch plans, o el encargado de la vista es el mismo que el de la persistencia, o los objetos deatached en la vista son una bomba de relojeria, en cuanto al encargado de la vista se le pase por la cabeza “anda voy a añadir tal dato aqui que quedaría bien” catacrash!!, y esto en mi opinión no es admisible en equipos de trabajo medios/grandes.

    En mi opinión, sea como sea, la sesión tiene que estar abierta mientras estoy en la vista para evitar estos objetos “bomba” :-P , el OSIV no es el colmo de la elegancia precisamente pero me parece una solución pragmatica y suficiente para la mayoría de los casos de uso habituales.

    Otra posible solución es crear DTO’s personalizados para cada vista, requiere más codificación pero me aseguro evitar errores por “lazy exception”. Yo por ejemplo utilizando GWT+Hibernate uso este sistema, claro que la vista realmente se pinta con AJAX en el navegador así que es obvio que para este caso no puedo dejar la sesión abierta mientras pinto la vista.

  5. 5 jmonne[en]gmail.com (jmonne)

    Siempre se puede “liberar” de usar OSVI configurando la transacciones con lo que una session Hibernate siempre estará disponible hasta el final de la transacción, por lo que podrás cargar las colecciones/atributos lazy que necesites dentro de esta.

    Respecto el tema del post la verdad es que la solución parece muy elegante.

    Un saludo.

  6. 6 batch4j

    Cuanto os complicais la vida por no utilizar EJB 3.0

Añade un Comentario





Close
E-mail It