io.github.dinamo541.corefx.persistence

EntityManagerHelper

Singleton final thread-safe provider-agnosticagnóstico al proveedor

Centralizes access to a single, shared persistence context (typically a JPA EntityManager) without coupling CoreFx to any persistence provider. Your application — which already has a JPA stack — registers a Supplier that produces the manager; CoreFx stores, lazily creates, caches and hands it back as an opaque object. Centraliza el acceso a un único contexto de persistencia compartido (típicamente un EntityManager de JPA) sin acoplar CoreFx a ningún proveedor de persistencia. Tu aplicación — que ya tiene un stack de JPA — registra un Supplier que produce el gestor; CoreFx lo almacena, crea de forma perezosa, cachea y lo devuelve como un objeto opaco.

Why a Supplier, not an EntityManagerPor qué un Supplier, no un EntityManager

CoreFx is a reusable library and must not force a particular persistence stack (Jakarta Persistence, Hibernate, EclipseLink…) onto its consumers. Referencing jakarta.persistence.EntityManager directly would drag that API onto every consumer's classpath. So — exactly like FlowController's theme applier — the consuming app passes a Supplier<?>, and the library depends only on the native Java platform. CoreFx es una librería reutilizable y no debe forzar un stack de persistencia concreto (Jakarta Persistence, Hibernate, EclipseLink…) sobre sus consumidores. Referenciar jakarta.persistence.EntityManager directamente arrastraría esa API al classpath de cada consumidor. Así que — exactamente como el aplicador de temas de FlowController — la app consumidora pasa un Supplier<?>, y la librería depende únicamente de la plataforma Java nativa.

Quick startInicio rápido

// Once at startup, in YOUR app where jakarta.persistence is available:
EntityManagerHelper.getInstance().initialize(() ->
        Persistence.createEntityManagerFactory("myPersistenceUnit")
                   .createEntityManager());

// Anywhere afterwards — typed retrieval, no cast:
EntityManager em = EntityManagerHelper.getInstance().getManager(EntityManager.class);
em.getTransaction().begin();
em.persist(entity);
em.getTransaction().commit();

LifecycleCiclo de vida

void initialize(Supplier<?> managerSupplier)

Registers the factory (called once). The supplier is invoked lazily — the first time a manager is actually requested, and again whenever the cached one is discarded. Throws NullPointerException if the supplier is null, and IllegalStateException if already initialized.Registra la fábrica (se llama una vez). El supplier se invoca de forma perezosa — la primera vez que realmente se solicita un gestor, y de nuevo cada vez que se descarta el cacheado. Lanza NullPointerException si el supplier es null, y IllegalStateException si ya está inicializado.

Object getManager()

Returns the shared manager, creating it from the supplier on first access (under a lock, so concurrent first-time callers share one instance). Throws IllegalStateException if not initialized, if the supplier returns null, or if it fails.Devuelve el gestor compartido, creándolo desde el supplier en el primer acceso (bajo un lock, para que los llamantes concurrentes de primera vez compartan una instancia). Lanza IllegalStateException si no está inicializado, si el supplier devuelve null, o si falla.

<T> T getManager(Class<T> type)

The recommended accessor. Returns the manager cast to T, so you work with your own type (e.g. EntityManager) without a cast. Throws if not initialized, the supplier misbehaves, or the created manager isn't an instance of type.El accesor recomendado. Devuelve el gestor casteado a T, para que trabajes con tu propio tipo (p. ej. EntityManager) sin cast. Lanza excepción si no está inicializado, si el supplier se comporta mal, o si el gestor creado no es una instancia de type.

void resetManager()

Discards the cached manager without closing it, so the next access rebuilds a fresh one. Ownership of the discarded instance returns to you — close it yourself if it holds resources, or use close() instead.Descarta el gestor cacheado sin cerrarlo, para que el siguiente acceso reconstruya uno nuevo. La propiedad de la instancia descartada vuelve a ti — ciérrala tú mismo si retiene recursos, o usa close() en su lugar.

void close()

Best-effort shutdown: if the cached manager is AutoCloseable (as JPA EntityManagers are), its close() is called; any exception is swallowed so shutdown never fails. The reference is cleared, but the supplier registration is kept — the helper stays usable.Cierre de mejor esfuerzo: si el gestor cacheado es AutoCloseable (como lo son los EntityManager de JPA), se llama a su close(); cualquier excepción se traga para que el apagado nunca falle. La referencia se limpia, pero el registro del supplier se mantiene — el helper sigue siendo usable.

boolean hasManager()  ·  boolean isInitialized()

Whether a manager is currently cached, and whether a supplier has been registered.Si hay un gestor cacheado actualmente, y si se ha registrado un supplier.

🔁
resetManager vs. closeresetManager vs. close

Use resetManager() to drop a stale/closed manager and let the next call rebuild one (you own cleanup of the old one). Use close() when you want the helper to close the manager for you. Both leave the supplier in place, so the helper keeps working afterward.Usa resetManager() para soltar un gestor obsoleto/cerrado y dejar que la siguiente llamada reconstruya uno (tú te encargas de limpiar el viejo). Usa close() cuando quieras que el helper cierre el gestor por ti. Ambos dejan el supplier en su lugar, así que el helper sigue funcionando después.

Lazy, thread-safe creationCreación perezosa y thread-safe

Initialization is guarded by double-checked locking and a volatile flag, mirroring FlowController. The manager is created lazily on first access under the same lock, so racing callers never build several instances. Reads after creation are lock-free. La inicialización está protegida por double-checked locking y un indicador volatile, reflejando a FlowController. El gestor se crea de forma perezosa en el primer acceso bajo el mismo lock, así los llamantes en carrera nunca construyen varias instancias. Las lecturas tras la creación son lock-free.

⚠️
A single shared manager — mind concurrencyUn único gestor compartido — cuida la concurrencia

By default this caches one manager for the whole process. A JPA EntityManager is not thread-safe, so a single shared instance suits a typical desktop app with one persistence context. If you need isolation (e.g. per task), call resetManager() between units of work, or manage separate managers outside this helper.Por defecto esto cachea un gestor para todo el proceso. Un EntityManager de JPA no es thread-safe, así que una única instancia compartida encaja con una app de escritorio típica con un solo contexto de persistencia. Si necesitas aislamiento (p. ej. por tarea), llama a resetManager() entre unidades de trabajo, o gestiona managers separados fuera de este helper.

RecommendationsRecomendaciones

Do call initialize exactly once at startup, then always retrieve via getManager(EntityManager.class) for a cast-free, type-checked handle.Llama a initialize exactamente una vez al inicio, y luego recupera siempre vía getManager(EntityManager.class) para un handle sin cast y con tipo comprobado.

Do call close() on application shutdown for a clean, best-effort release.Llama a close() al apagar la aplicación para una liberación limpia y de mejor esfuerzo.

Do recover from a closed/stale context with resetManager() rather than re-initializing (which would throw).Recupérate de un contexto cerrado/obsoleto con resetManager() en vez de reinicializar (lo que lanzaría excepción).