5.3. Conexión#
En el apartado dedicado a operaciones básicas ya adelantamos
que la creación del objeto EntityManagerFactory es muy
costosa, puesto que implica el establecimiento y validación de la conexión. Por
consiguiente, conviene asegurarnos de que su creación sólo se realiza una vez
para lo cual nos valdremos del patrón Singleton modificado para crear una
clase con utilidades
:
public class JpaEMUtils {
private static Map<Integer, EntityManagerFactory> instances = new HashMap<>();
private JpaEMUtils() { super(); }
/**
* Genera siempre el mismo EntityManagerFactory a partir del nombre de la unidad de persistencia
* y un mapa que modifica sus propiedades.
* @param persistenceUnit El nombre de la unidad de persistencia.
* @param props El mapa que define las propiedades definidas en tiempo de ejecución.
* @return El objeto resultante.
*/
public static EntityManagerFactory getEntityManagerFactory(String persistenceUnit, Map<String, String> props) {
if(persistenceUnit == null) return getEntityManagerFactory();
int hashCode = Objects.hash(persistenceUnit, props);
EntityManagerFactory instance = instances.get(hashCode);
if(instance == null || !instance.isOpen()) {
instance = Persistence.createEntityManagerFactory(persistenceUnit, props);
instances.put(hashCode, instance);
}
return instance;
}
/**
* Genera siempre el mismo EntityManagerFactory a partir del nombre de la unidad de persistencia.
* Se sobreentiende que no se modifica o añade ninguna propiedad.ñ
* @param persistenceUnit El nombre de la unidad de persistencia.
* @return El objeto resultante.
*/
public static EntityManagerFactory getEntityManagerFactory(String persistenceUnit) {
return getEntityManagerFactory(persistenceUnit, null);
}
/**
* Devuelve un objeto EntityManagerFactory generado anteriormente. Sólo funciona si se generó uno.
* @return El objeto resultante.
*/
public static EntityManagerFactory getEntityManagerFactory() {
EntityManagerFactory instance = null;
switch(instances.size()) {
case 1:
instance = instances.values().iterator().next();
if(instance.isOpen()) return instance;
else instances.clear();
case 0:
throw new IllegalStateException("No hay disponible ninguna instancia con parámetros");
default:
throw new IllegalStateException("Invocación ambigua: hay varios candidatos");
}
}
/**
* Elimina todos los objetos previamente creados.
*/
public static void reset() {
instances.clear();
}
// Transacciones.
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);
}
// Fin transacciones.
}
La clase:
Implementa un patrón Singleton, de modo que para la misma configuración de conexión, siempre se devuelve la misma fábrica.
En caso de que sólo se haya creado una fábrica, es posible recuperarla sin necesidad de facilitar la configuración.
Nota
Los métodos dedicados a las transacciones los trararemos más adelante.
De este modo:
public static main(String[] args) throws Exception {
// Propiedades configuradas en tiempo de ejecución.
Map<String, String> props = new HashMap<>();
props.put("jakarta.persistence.jdbc.url", "jdbc:sqlite:centro.db");
props.put("hibernate.show_sql", "true");
try(EntityManagerFactory emf = JpaEmFactory.getInstance("MiUnidadP", props)) {
operaciones();
}
}
private static operaciones() {
// ¡Ojo! No hay que cerrarlo.
emf = JpaEmFactory.getInstance(); // Devuelve el mismo objeto.
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);
}
}
}