Transacciones

5.5. Transacciones#

Ya se mostró como realizar transacciones en el ejemplo que ilustraba el uso básico del ORM siguiendo la especificación JPA:

try(em EntityFactory = emf.createEntityManager()) {
   EntityTransaction tr = em.getTransaction();
   try {
      tr.begin();
      Centro centro = new Centro(11004866, "IES Castillo de Luna", Centro.Titularidad.PUBLICA);
      em.persist(centro);
      tr.commit();
   }
   catch(Exception e) {
      if(tr != null && tr.isActive()) tr.rollback();
      throw new RuntimeException("Error al almacenar el centro", err);
   }
}

Tuvimos que introducirlas tan temprano porque JPA nos obliga a explicitar la transacción siempre que se realioza alguna operación que implica cambios en la base de datos. Ciertamente la sintaxis es algo farragosa, por lo que podríamos definir algunos métodos en JpaEMUtils que simplificaran su creación:

public static void transaction(Consumer<EntityManager> action, String persistenceUnit, Map<String, String> props) {
    EntityManagerFactory emf = getEntityManagerFactory(persistenceUnit, props);
    try(EntityManager em = emf.createEntityManager()) {
        EntityTransaction tx = em.getTransaction();
        try {
            tx.begin();
            action.accept(em);
            tx.commit();
        }
        catch(Exception e) {
            if(tx != null && tx.isActive()) tx.rollback();
            throw new RuntimeException("Fallo en la transacción", e);
        }
    }
}

public static void transaction(Consumer<EntityManager> action, String persistenceUnit) {
    transaction(action, persistenceUnit, null);
}

public static void transaction(Consumer<EntityManager> action) {
    transaction(action, null, null);
}

La existencia de los tres métodos responde a las mismas causas por las que hicimos varias versiones de .getEntityManagerFactory. Si manejamos una sola fábrica y ya la creamos antes, que será lo habitual, sólo necesitaremos pasar la acción:

JpaEMUtils.transaction(em -> {
   Centro castillo = new Centro(11004866, "IES Castillo de Luna", Centro.Titularidad.PUBLICA);
   em.persist(castillo);
});

Este método simplifica las operaciones en la base de datos, pero a costa de que los objetos creados dentro de la expresión lambda, no son visibles fuera de ella.

Hay otros métodos que están asociados al objeto EntityManager:

em.flush()

Es un método que hace persistentes los cambios obrados hasta ese momento, pero que a diferencia de tx.commit() no cierra la transacción.

em.clear()

Desvincula todos los objetos asociados. Un efecto colateral es que ninguno de los cambios que haya sufrido, se aplicará al hacer un commit (o un flush).

em.detach(Object entity)

Desvincula el objeto proporcionado como argumento.