java - persistencecontext - entitymanager hibernate
Cómo administrar el ciclo de vida de EntityManager en un entorno CDI(utilizando Tomcat) (4)
Estoy desarrollando una aplicación y comencé a usar CDI
junto con JSF
y JPA
. El contenedor web es Tomcat
.
Estoy muy confundido sobre el EntityManager
vida de EntityManager
en mis CDI
beans y necesitaría un buen consejo para aclarar algunas cosas en mi mente. En general, lo que he leído es que EntityManager
debe usar principalmente en un contenedor Java EE
, inyectándolo usando la anotación PersistenceContext
. Entonces el contenedor cuida su vida. Sin embargo, si no usa el contenedor Java EE
(como Tomcat
), entonces necesito administrar la EntityManager
mi EntityManager
.
¿Cuáles son mis mejores opciones ahora, usando Tomcat, CDI, JSF and JPA
? Lo que estoy haciendo actualmente es lo siguiente:
public class EntityManagerFactoryProducer {
public static final String TEST = "test";
@Produces
@ApplicationScoped
public EntityManagerFactory create() {
return Persistence.createEntityManagerFactory(TEST);
}
public void destroy(@Disposes
EntityManagerFactory factory) {
factory.close();
}
}
public class EntityManagerProducer {
@Inject
private transient Logger logger;
@Inject
private EntityManagerFactory emf;
@Produces
public EntityManager create() {
return emf.createEntityManager();
}
public void destroy(@Disposes
EntityManager em) {
em.close();
logger.debug(String.format("%s Entity manager was closed", em));
}
}
@Named
@ViewScoped
@Interceptors(LoggingInterceptor.class)
public class ProductBacking implements Serializable {
@Inject
private ProductDAO productDAO;
@ViewScoped
public class ProductDAOImpl implements ProductDAO, Serializable {
private static final long serialVersionUID = 4806788420578024259L;
private static final int MAX_RANDOMIZED_ELEMENTS = 3000;
@Inject
private transient Logger logger;
@Inject
private EntityManager entityManager;
@Override
public List<Product> getSuggestedProducts() {
logger.debug(String.format("%s Entity manager get products", entityManager));
return entityManager.createQuery("SELECT p FROM Product p ORDER BY random()", Product.class).setMaxResults(
MAX_RANDOMIZED_ELEMENTS).getResultList();
}
@Override
public void saveProduct(Product product) {
logger.debug(String.format("%s Entity manager save product", entityManager));
entityManager.getTransaction().begin();
entityManager.merge(product);
entityManager.getTransaction().commit();
}
@PreDestroy
void destroy() {
entityManager.close();
}
}
Así que básicamente estoy usando CDI
simple para lograr esto. Sin embargo, no estoy seguro de si esta es una forma estándar de hacerlo, y lo que es más importante, no sé cómo cerrar el EntityManager
después de que la vida del bean haya terminado. Como resumen: termino con muchas conexiones no cerradas ( EntityManager
), así que hay una pérdida de memoria.
¿Puede alguien ayudarme a entender cómo debo proceder? Muchas gracias.
El principal problema es que el administrador de su entidad no tiene alcance. Como resultado, es dependiente que nunca se limpia. Debe proporcionar un alcance para el administrador de su entidad.
Lo otro es que Apache DeltaSpike ya lo tiene resuelto. ¿Por qué no usar DeltaSpike? https://deltaspike.apache.org/documentation/jpa.html
No se trata de CDI. El ciclo de vida de EntityManager depende de su tipo, que puede ser:
- transaccional gestionado por contenedor,
- gestionado por contenedores extendido,
- gestionado por la aplicación.
Los dos primeros solo están disponibles en un servidor de aplicaciones completo. Entonces, si vas a seguir con un contenedor de servlets, estarás limitado a la tercera opción.
Tendrá que abrir y cerrar explícitamente los EM en su aplicación. Es sencillo: cree una instancia de EntityManagerFactory para toda la aplicación, inyecte en todos sus beans. Cuando necesite un EM, simplemente créelo, úselo y luego ciérrelo inmediatamente sin esperar a que finalice el contexto de su bean. Debido a que en esta configuración, un EntityManager abierto mantendrá una conexión y con beans de larga duración se quedará sin conexiones. Puede encontrar una explicación fácil y completa en la sección Cómo obtener una conexión de base de datos JPA del manual de ObjectDB.
Puede mantener el estado del bean CDI utilizando los alcances de CDI . En realidad, EntityManagerProducer#create()
no tiene el alcance. Cualquiera que sea el RI de CDI que haya configurado / instalado en Tomact, ya sea en Weld o en OpenWebBean, puede definir su estado de frijol cdi como belwo.
@Produces @RequestScoped
public EntityManager create() {
return emf.createEntityManager();
}
Tu problema son
1. CDI, JSF and JPA2.
2. Managing EntityManager lifecycle when using JPA in a non enterprise environment (e.g. Tomcat)
1. CDI, JSF y JPA2.
Tomcat no es compatible con el CDI de la caja, ni siquiera el JSF, ya que los desarrolladores tuvieron que proporcionar el JSF. JSF 2.2 tiene nuevos scops compatibles con el CDI @ViewScoped. para @ManagedBean.
(1) Realmente, si está más interesado en utilizar CDI o CDI + JSF + JPA, actualice Tomcat a TomEE o vaya con TomEE. Tomcat + Java EE = TomEE. La edición Java Enterprise de Tomcat, con TomEE obtienes Tomcat con JPA.
(2) Si no tiene control sobre la actualización del servidor Tomcat, en ese caso tenía que hacerlo yo. Suministre CDI y algunos otros archivos jar y de configuración junto con weapp it self. ii. Instalación de CDI en tomcat (Weld o OpenWebBeans, ambas son implementaciones de CDI principales)
(3) Tomcat 8. Tomcat 8 está alineado con Java EE 7.
2) Gestión del ciclo de vida de EntityManager
La gestión del ciclo de vida de EntityManager cuando se utiliza JPA en un entorno no empresarial (por ejemplo, Tomcat) o Java SE es una tarea personalizada. En esta situación, debe considerar el alcance correcto del EntityManager a utilizar y, al trabajar con recursos, siempre es importante asegurarse de que estén cerrados cuando ya no sean necesarios.
There are three main types of EntityManagers defined in JPA.
Container Managed and Transaction Scoped Entity Managers
Container Managed and Extended Scope Entity Managers
Application Managed Entity Managers
Trabajando con JPA hay dos tipos de recursos que podemos cuidar: EntityManager y transacciones. En este caso, debe considerar el alcance correcto del EntityManager a usar.
1. An EntityManager is not a heavyload object.
There is no need to use the same EntityManger longer than needed,
You can''t use an EntityManager instance for the whole application lifecycle (application scope) for the EntityManager is not Thread-safe)
2. It''s not safe to traverse lazy-loaded relationships once the EntityManager is closed (This situation will change as of JPA 2.0).
i.)Method scope (i.e. instantiate/destroy one EntityManager in each business method).
The method scope is not enough for every situation. There could be some scenarios where you''ll need a wide scope, such as the following situations:
i. When transactions spread multiple business methods.
ii. Need to traverse lazy-loaded relationships outside a method (e.g. in a JSF page).
In method scope be careful to ensure the EntityManger is always closed
ii.)Request scope (on-demand creation of the EntityManager instance to use within the request service)
EntityManager per HTTP request strategy with the following features:
i. Creation on demand of the EntityManager.
ii. Lazy closing of the EntityManager.
The main benefit of this scope is derived from the delayed closing of the EntityManager (it will last as long as a HTTP request is in process).
Every queried entity will be managed till the end of the request and therefore during the presentation phase (the render phase in JSF for instance).
En su caso, está utilizando el gestor de entidad de aplicación y la transacción administrada por la aplicación, es decir, su código que debe manejar la transacción. En pocas palabras significa:
Llama:
entityManager.getTransaction().begin(); //to start a transaction
Entonces, si tienes éxito, te asegurarás de llamar.
entityManager.getTranasaction().commit(); //to commit changes to database
o en caso de fallo se asegurará de llamar a:
entityManager.getTransaction().rollBack();
Ahora imagine que tiene un contenedor, que sabe cuándo llamar a begin(), commit() or rollback()
, esa es una transacción administrada por contenedor.
Puedes configurar tres tipos de EM
container-managed transactional,
container-managed extended,
application-managed.
Usualmente usamos transacciones administradas por contenedores y administradas por aplicaciones. Te daré ejemplos.
Para aplicaciones administradas, generalmente definimos un EM dentro de un método.
public List<BookingMainDO> retrieve(String key) {
...
EntityManager em = null;
try {
em = emf.createEntityManager();
Query query = em.createQuery(queryString);
//get the resultList of BookingMain
result = query.getResultList();
} catch (Exception e) {
DAOExceptionHandler.handler(dataSource,BookingMainDAO.class, e, queryString);
}finally{
em.close();
}
...
}
para el contenedor gestionado EM, el valor predeterminado es el ámbito de la transacción. Necesitas configurar en la primavera con la siguiente anotación.
<context:annotation-config/>
Luego en su clase DAO agregue la siguiente anotación
@PersistenceContext
private EntityManager em;
Luego, en cada método, puede utilizar el EM auto inyectado. con la transacción con ámbito EM, el persistenceContext puede propagarse entre diferentes métodos dentro de la misma transacción.