Hibernate SessionFactory frente a JPA EntityManagerFactory
Soy nuevo en Hibernate y no estoy seguro de si debo usar Hibernate SessionFactory
o JPA EntityManagerFactory
para crear un Hibernate Session
.
¿Cuál es la diferencia entre estos dos? ¿Cuáles son las ventajas y desventajas de usar cada uno de ellos?
Prefiero EntityManagerFactory
y EntityManager
. Están definidos por el estándar JPA.
SessionFactory
y Session
son específicos de hibernación. Invoca EntityManager
la sesión de hibernación bajo el capó. Y si necesitas algunas funcionalidades específicas que no están disponibles en el EntityManager
, puedes obtener la sesión llamando a:
Session session = entityManager.unwrap(Session.class);
SessionFactory
vs.EntityManagerFactory
Como expliqué en la Guía del usuario de Hibernate , Hibernate SessionFactory
extiende el JPA EntityManagerFactory
, como lo ilustra el siguiente diagrama:
Entonces, SessionFactory
también es un JPA EntityManagerFactory
.
Tanto el SessionFactory
como el EntityManagerFactory
contienen los metadatos de mapeo de entidades y le permiten crear un Hibernate Session
o un EntityManager
.
Session
vs.EntityManager
Al igual que SessionFactory
y EntityManagerFactory
, Hibernate Session
extiende el JPA EntityManager
. Entonces, todos los métodos definidos por EntityManager
están disponibles en Hibernate Session
.
El Session
y `EntityManager traducen las transiciones de estado de la entidad en declaraciones SQL, como SELECT, INSERT, UPDATE y DELETE.
Hibernación frente a arranque JPA
Al iniciar una aplicación JPA o Hibernate, tiene dos opciones:
- Puede iniciar a través del mecanismo nativo de Hibernate y crear un archivo
SessionFactory
a través del archivoBootstrapServiceRegistryBuilder
. Si está utilizando Spring, el arranque de Hibernate se realiza a través deLocalSessionFactoryBean
, como se ilustra en este ejemplo de GitHub . - O puede crear un JPA
EntityManagerFactory
a través de laPersistence
clase o el archivoEntityManagerFactoryBuilder
. Si está utilizando Spring, el arranque de JPA se realiza a través deLocalContainerEntityManagerFactoryBean
, como se ilustra en este ejemplo de GitHub .
Se prefiere el arranque a través de JPA. Esto se debe a que JPA FlushModeType.AUTO
es una opción mucho mejor que el legado FlushMode.AUTO
, que rompe la coherencia de lectura y escritura para consultas SQL nativas .
Desenvolviendo JPA para hibernar
Además, si inicia a través de JPA y ha inyectado a EntityManagerFactory
través de la @PersistenceUnit
anotación:
@PersistenceUnit
private EntityManagerFactory entityManagerFactory;
Puede obtener acceso fácilmente al subyacente Sessionfactory
utilizando el unwrap
método:
SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class);
Lo mismo se puede hacer con la APP EntityManager
. Si inyecta a EntityManager
través de la @PersistenceContext
anotación:
@PersistenceContext
private EntityManager entityManager;
Puede obtener acceso fácilmente al subyacente Session
utilizando el unwrap
método:
Session session = entityManager.unwrap(Session.class);
Conclusión
Por lo tanto, debe iniciar a través de JPA, usar EntityManagerFactory
y EntityManager
y solo desenvolverlos en sus interfaces de Hibernate asociadas cuando desee obtener acceso a algunos métodos específicos de Hibernate que no están disponibles en JPA, como buscar la entidad a través de su identificador natural .
Quiero agregar a esto que también puedes obtener la sesión de Hibernate llamando al getDelegate()
método desde EntityManager
.
ex:
Session session = (Session) entityManager.getDelegate();
Prefiero la EntityManager
API JPA2 a SessionFactory
, porque parece más moderna. Un ejemplo sencillo:
APP:
@PersistenceContext
EntityManager entityManager;
public List<MyEntity> findSomeApples() {
return entityManager
.createQuery("from MyEntity where apples=7", MyEntity.class)
.getResultList();
}
Fábrica de sesiones:
@Autowired
SessionFactory sessionFactory;
public List<MyEntity> findSomeApples() {
Session session = sessionFactory.getCurrentSession();
List<?> result = session.createQuery("from MyEntity where apples=7")
.list();
@SuppressWarnings("unchecked")
List<MyEntity> resultCasted = (List<MyEntity>) result;
return resultCasted;
}
Creo que está claro que el primero parece más limpio y también es más fácil de probar porque se puede burlar fácilmente de EntityManager.