En Hibernate, la interfaz Session es fundamental para interactuar con la base de datos y gestionar el ciclo de vida de los objetos. Este artículo explora métodos clave para manipular entidades, incluyendo diferencias en comportamiento y técnicas para optimizar las actualizaciones.
Métodos load() y get()
El método load() devuelve un proxy del objeto y utiliza carga diferida; solo emite una consulta SQL cuando se accede a sus propiedades. Si no existe un registro con el identificador proporcionado, lanza una excepción. En contraste, get() recupera el objeto inmediatamente con una consulta SQL y devuelve null si no se encuentra.
Para operaciones como eliminación, donde solo se necesita una referencia, load() es adecuado. Para acceder a propiedades del objeto, se recomienda get().
@Test
public void pruebaCargarEmpleado() {
Empleado emp = (Empleado) session.load(Empleado.class, Long.valueOf(10));
// Si el ID 10 no existe, se lanza una excepción.
System.out.println(emp.getNombre());
}
@Test
public void pruebaObtenerEmpleado() {
Empleado emp = (Empleado) session.get(Empleado.class, Long.valueOf(10));
// Si el ID 10 no existe, imprime null.
System.out.println(emp);
}
Método update()
Este método sincroniza el estado de un objeto desvinculado con la base de datos. Si el objeto es transitorio, puede generar errores a manos que tenga un identificador asignado que coincida con un registro existetne.
Por defecto, update() actualiza todos los campos del objeto, incluso si solo uno ha cambiado. Para evitar actualizaciones innecesarias, se puede habilitar dynamic-update en la configuración XML de la entidad, lo que permite generar sentencias SQL que solo actualicen los campos modificados.
Sin embargo, en actualizaciones entre sesiones, update() no puede comparar el estado del objeto con la caché de la nueva sesión, por lo que actualiza todos los campos. Una alternativa es usar HQL para un control preciso.
@Test
public void pruebaActualizarEmpleado() {
Session sesion1 = sessionFactory.openSession();
sesion1.beginTransaction();
Empleado emp = (Empleado) sesion1.get(Empleado.class, Long.valueOf(5));
sesion1.getTransaction().commit();
sesion1.close();
Session sesion2 = sessionFactory.openSession();
sesion2.beginTransaction();
emp.setNombre("Nuevo Nombre");
sesion2.update(emp);
sesion2.getTransaction().commit();
sesion2.close();
}
Configuración para Actualizaciones Dinámicas
Para reducir el impacto en el rendimiento, se puede definir dynamic-update="true" en el mapeo XML de la entidad. Esto asegura que solo los campos alterados se incluyan en la sentencia SQL de actualización.
Uso de merge() para Actualizaciones entre Sesiones
Cuando se trabaja con objetos desvinculados, merge() puede evitar conflictos de caché. Este método compara el objeto proporcionado con el estado actual en la base de datos mediante una consulta previa, y luego aplica los cambios solo en los campos modificados.
@Test
public void pruebaFusionarEmpleado() {
Session sesion1 = sessionFactory.openSession();
sesion1.beginTransaction();
Empleado emp = (Empleado) sesion1.get(Empleado.class, Long.valueOf(5));
sesion1.getTransaction().commit();
sesion1.close();
emp.setNombre("Nombre Actualizado");
Session sesion2 = sessionFactory.openSession();
sesion2.beginTransaction();
sesion2.merge(emp); // Emite un SELECT y luego un UPDATE con campos modificados.
sesion2.getTransaction().commit();
sesion2.close();
}
Método saveOrUpdate()
Este método elige entre save() y update() basándose en el estado del objeto. Si el objeto es transitorio, ejecuta save(); si es desvinculado, ejecuta update().
@Test
public void pruebaGuardarOActualizar() {
Session sesion1 = sessionFactory.openSession();
sesion1.beginTransaction();
Departamento dept = (Departamento) sesion1.get(Departamento.class, Long.valueOf(2));
sesion1.getTransaction().commit();
sesion1.close();
Session sesion2 = sessionFactory.openSession();
sesion2.beginTransaction();
dept.setNombre("Ventas Actualizado");
Departamento nuevoDept = new Departamento();
nuevoDept.setNombre("Investigación");
sesion2.saveOrUpdate(dept); // Objeto desvinculado: ejecuta update.
sesion2.saveOrUpdate(nuevoDept); // Objeto transitorio: ejecuta save.
sesion2.getTransaction().commit();
sesion2.close();
}
Resolución de Conflictos con merge()
Si update() encuentra un conflicto de identificador en la caché de la sesión (por ejemplo, dos objetos con el mismo OID), puede producir un error. merge() soluciona esto fusionando los atributos del objeto proporcionado con el objeto existente en la caché, y luego actualizando la base de datos.
@Test
public void pruebaFusionarParaEvitarConflictos() {
Session sesion1 = sessionFactory.openSession();
sesion1.beginTransaction();
Departamento dept = (Departamento) sesion1.get(Departamento.class, Long.valueOf(2));
sesion1.getTransaction().commit();
sesion1.close();
Session sesion2 = sessionFactory.openSession();
sesion2.beginTransaction();
Departamento deptEnCaché = (Departamento) sesion2.get(Departamento.class, Long.valueOf(2));
dept.setNombre("Nuevo Departamento");
sesion2.merge(dept); // Fusiona cambios sin errores de caché.
sesion2.getTransaction().commit();
sesion2.close();
}
Método delete()
Este método marca un objeto para eliminación. Se puede usar load() para obtener una referencia diferida, ya que no se necesitan acceder a las propiedades. La eliminación real ocurre al confirmar la transacción.
@Test
public void pruebaEliminarEmpleado() {
Empleado emp = (Empleado) session.load(Empleado.class, Long.valueOf(1));
session.delete(emp);
session.getTransaction().commit();
session.close();
}