namedquery manager example ejemplos curso configuracion java java-ee jpa cdi jta

java - manager - jpa configuracion



¿Por qué diferentes unidades de persistencia con fuentes de datos separadas consultan la misma fuente de datos? (2)

Estoy desarrollando una aplicación web que necesita acceso a dos servidores de bases de datos diferentes (H2 y Oracle). El contenedor es un Apache Tomee 1.5.1 y estoy usando la pila Java EE con las bibliotecas incluidas (JSF, JPA, CDI, EJB, etc.).

Intento utilizar dos administradores de entidades dentro de una transacción XA para extraer datos de la base de datos Oracle y conservarlos en el H2 después de transformarlos, PERO todas las consultas se ejecutan contra la base de datos H2 sin importar el administrador de entidades que uso. ¿Alguna ayuda?

EDITAR : descubrí que si trato de acceder a los administradores de entidades en orden inverso, su comportamiento es el mismo, pero accediendo a Oracle. Es decir: los administradores de la entidad permanecen con la primera base de datos a la que se accede.

El EJB donde sucede esto (llamando a service.getFoo() desde JSF):

@Named @Stateless public class Service { @Inject @OracleDatabase private EntityManager emOracle; @Inject @H2Database private EntityManager emH2; @TransactionAttribute(TransactionAttributeType.REQUIRED) public List<Foo> getFoo() { TypedQuery<Foo> q = emH2.createQuery( "SELECT x FROM Foo f", Foo.class); List<Foo> l = q.getResultList(); if (l == null || l.isEmpty()) { update(); } return q.getResultList(); } @TransactionAttribute(TransactionAttributeType.REQUIRED) public void update() { // FAIL: This query executes against H2 with Oracle entity manager! List<Object[]> l = emOracle.createNativeQuery("SELECT * FROM bar ").getResultList(); //more stuff... } }

El productor de recursos (CDI) para los administradores de entidades (donde @ H2Database y @OracleDatabase son qualifiers ):

public class Resources { @Produces @PersistenceContext(unitName = "OraclePU") @OracleDatabase private EntityManager emOracle; @Produces @PersistenceContext(unitName = "H2PU") @H2Database private EntityManager emH2; }

Mi peristence.xml se ve así:

<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <persistence-unit name="H2PU" transaction-type="JTA"> <provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider> <jta-data-source>H2DS</jta-data-source> <class>my.app.h2.Foo</class> <exclude-unlisted-classes>true</exclude-unlisted-classes> </persistence-unit> <persistence-unit name="OraclePU" transaction-type="JTA"> <provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider> <jta-data-source>OracleDS</jta-data-source> <class>my.app.oracle.Bar</class> <exclude-unlisted-classes>true</exclude-unlisted-classes> </persistence-unit> </persistence>

Y finalmente, las fuentes de datos dentro de tomee.xml (no hay otras fuentes de datos configuradas dentro de este archivo):

<Resource id="OracleDS" type="javax.sql.DataSource"> jdbcDriver = oracle.jdbc.xa.client.OracleXADataSource jdbcUrl = jdbc:oracle:thin:@server:port:instance jtaManaged = true password = abcde userName = user </Resource> <Resource id="H2DS" type="javax.sql.DataSource"> jdbcDriver=org.h2.jdbcx.JdbcDataSource jdbcUrl=jdbc:h2:h2/db;AUTO_SERVER=TRUE jtaManaged = true password = edcba userName = user </Resource>


Intente primero crear una consulta Que no sea una consulta nativa, devolviendo una lista de barras. Intenta también comentar la inyección de H2 en tu EJB. Si funciona, entonces sabrá que es un problema de conflicto CDI.


Contextos de persistencia administrados por contenedor

Al utilizar contextos de persistencia gestionados por contenedor (como lo está a través de anotaciones @PersistenceContext), la especificación JPA especifica que solo se puede asociar UN contexto de persistencia con una transacción JTA.

El contexto de persistencia es creado por el contenedor Java EE. A pesar de las apariencias del código (las anotaciones de @PersistenceContext parecen sugerir que la PC se inyecta directamente en las variables de instancia de EntityManager), el contexto de persistencia se almacena realmente como referencia DENTRO DE LA TRANSACCIÓN DE JTA. Cada vez que se produce una operación de EntityManager, no se refiere a su propio contexto de persistencia interna. En su lugar, realiza una operación especial porque está gestionada por contenedor: siempre busca el contexto de persistencia dentro de la transacción JTA y lo usa. Esto se llama propagación de contexto de persistencia JTA.

Algunas citas de la especificación JPA:

Cuando se utiliza un administrador de entidades gestionado por contenedor, el ciclo de vida del contexto de persistencia siempre se gestiona automáticamente, de forma transparente para la aplicación, y el contexto de persistencia se propaga con la transacción JTA.

Contexto de persistencia con ámbito de transacción gestionado por contenedor

... Un nuevo contexto de persistencia comienza cuando el administrador de la entidad gestionada por contenedor se invoca [76] en el ámbito de una transacción JTA activa, y no existe un contexto de persistencia actual ya asociado con la transacción JTA. El contexto de persistencia se crea y luego se asocia con la transacción JTA.

Contexto de persistencia extendida administrado por contenedor

... Un contexto de persistencia extendida administrado por contenedor solo se puede iniciar dentro del alcance de un bean de sesión con estado. Existe desde el punto en el que se crea el bean de sesión con estado que declara una dependencia en un administrador de entidades de tipo PersistenceContextType.EXTENDED, y se dice que está vinculado al bean de sesión con estado. La dependencia en el contexto de persistencia extendida se declara mediante el elemento descriptor de despliegue PersistenceContext o Persistence-Context-Ref. El contenedor cierra el contexto de persistencia cuando se completa el método @Remove del bean de sesión con estado (o se destruye la instancia de bean de sesión con estado).

Requisitos para la propagación del contexto de persistencia

... Si se llama un componente y no hay transacción JTA ..., el contexto de persistencia no se propaga. • La invocación de un administrador de entidades definido con PersistenceContext-Type.TRANSACTION dará como resultado el uso de un nuevo contexto de persistencia. • La invocación de un administrador de entidades definido con PersistenceContext-Type.EXTENDED dará como resultado el uso del contexto de persistencia extendida existente vinculado a ese componente.

... Si se llama un componente y la transacción JTA se propaga en ese componente: • Si el componente es un bean de sesión con estado al que se ha vinculado un contexto de persistencia extendido y hay un contexto de persistencia diferente vinculado a la transacción JTA, un EJBException es arrojado por el contenedor. • De lo contrario, si hay un contexto de persistencia vinculado a la transacción JTA, ese contexto de persistencia se propaga y utiliza.

Entonces ese es tu problema La obvia pregunta de $ 64: ¿POR QUÉ pide la especificación para esto?

Bueno, es porque se trata de una compensación deliberada que proporciona una poderosa magia de EntityManager a los EJB.

El uso de transacciones JTA para propagar un único contexto de persistencia tiene una limitación: las transacciones no pueden abarcar múltiples contextos de persistencia, por lo que no pueden abarcar múltiples bases de datos.

Sin embargo, también tiene una gran ventaja: cualquier entityManager declarado en EJB puede compartir automáticamente el mismo contexto de persistencia y, por lo tanto, puede operar en el mismo conjunto de entidades JPA y participar en la misma transacción. Puede tener una cadena de EJB llamando a otros EJB de cualquier complejidad y todos se comportan de manera razonable y consistente contra los datos de la entidad JPA. Y tampoco necesitan la complejidad de intializar / compartir constantemente las referencias del administrador de entidades a través de las invocaciones de métodos: los gestores de entidades se pueden declarar de forma privada en cada método. La lógica de implementación puede ser muy simple.

La respuesta a su problema: utilice contextos de persistencia administrados por la aplicación (a través de EntityManagers administrados por la aplicación )

Declare su entityManager a través de uno de estos enfoques:

// "Java EE style" declaration of EM @PersistenceUnit(unitName="H2PU") EntityManagerFactory emfH2; EntityManager emH2 = emfH2.createEntityManager();

O

// "JSE style" declaration of EM EntityManagerFactory emfH2 = javax.persistence.Persistence.createEntityManagerFactory("H2PU"); EntityManager emH2 = emfH2.createEntityManager(); and the same for emfOracle & emOracle.

Debe llamar a em.close () cuando haya terminado con cada EM, preferiblemente mediante una cláusula {} final o mediante una declaración de Java 7 try-with-resources.

Los EM administrados por la aplicación aún participan en (es decir, se sincronizan con) las transacciones de JTA. Cualquier número de EM gestionados por la aplicación puede participar en una sola transacción JTA, pero ninguno de ellos tendrá su contexto de persistencia asociado o propagado a ningún EM administrado por contenedor .

Si el EntityManager se crea fuera del contexto de una transacción JTA (antes de que comenzara la transacción), entonces debe pedirle explícitamente que se una a la transacción JTA:

// must be run from within Java EE code scope that already has a JTA // transaction active: em.joinTransaction();

O incluso más simple, si el EntityManager se crea dentro del contexto de una transacción JTA, entonces el EntityManager administrado por la aplicación se une automáticamente a la implícita transacción de JTA, no es necesario joinTransaction ().

Por lo tanto, los EM administrados por la aplicación pueden tener una transacción JTA que se extiende a múltiples bases de datos. Por supuesto, siempre puede ejecutar una transacción JDBC de recursos locales independiente de JTA:

EntityTransaction tx = em.getTransaction(); tx.begin(); // .... tx.commit();

EDITAR: DETALLES ADICIONALES para la gestión de transacciones con administradores de entidades administradas por la aplicación

ADVERTENCIA: los ejemplos de código a continuación son para uso educativo: los he escrito a máquina para ayudar a explicar mis puntos y no he tenido tiempo de compilar / depurar / probar.

El parámetro predeterminado @TransactionManagement para EJB es TransactionManagement.CONTAINER y el parámetro predeterminado @TransactionAttribute para los métodos EJB es TransactionAttribute.REQUIRED.

Hay cuatro permutaciones para la gestión de transacciones:

  • A) EJB con transacciones gestionadas JTA CONTAINER

    Este es el enfoque preferido de Java EE.
    Anotación EJB clase @TransactionManagement:
    debe establecerse en TransactionManagement.CONTAINER explícitamente u omitirlo para utilizar implícitamente el valor predeterminado.
    Método EJB @TransactionAttribute annotation: debe establecerse en TransactionAttribute.REQUIRED explícitamente u omitirlo para que implicity use el valor predeterminado. (Nota: si tuviera un escenario comercial diferente, podría usar TransactionAttribute.MANDATORY o TransactionAttribute.REQUIRES_NEW si su semántica coincide con sus necesidades).
    Administradores de entidades administradas por aplicaciones:
    deben crearse mediante Persistence.createEntityManagerFactory ("unitName") y emf.createEntityManager (), como se describe arriba.
    Únase a EntityManagers con la transacción JTA:
    Cree los gestores de entidades DENTRO de un método EJB transaccional y se unirán automáticamente a la transacción JTA. O si se crean EntityManagers de antemano, llame a em.joinTransaction () dentro de un método EJB de transacción.
    Llame a EntityManager.close () cuando haya terminado de usarlos. Eso debería ser todo lo que se requiere.

    Ejemplos básicos: solo use más EntityManagers para transacciones en múltiples DB:

    @Stateless public class EmployeeServiceBean implements EmployeeService { // Transactional method public void createEmployee() { EntityManagerFactory emf = Persistence.createEntityManagerFactory("EmployeeService"); EntityManager em = emf.createEntityManager(); Employee emp = ...; // set some data // No need for manual join - em created in active tx context, automatic join: // em.joinTransaction(); em.persist(emp); // other data & em operations ... // call other EJBs to partake in same transaction ... em.close(); // Note: em can be closed before JTA tx committed. // Persistence Context will still exist & be propagated // within JTA tx. Another EM instance could be declared and it // would propagate & associate the persistence context to it. // Some time later when tx is committed [at end of this // method], Data will still be flushed and committed and // Persistence Context removed . emf.close(); } } @Stateful public class EmployeeServiceBean implements EmployeeService { // Because bean is stateful, can store as instance vars and use in multiple methods private EntityManagerFactory emf; private EntityManager em; @PostConstruct // automatically called when EJB constructed and session starts public void init() { emf = Persistence.createEntityManagerFactory("EmployeeService"); em = emf.createEntityManager(); } // Transactional method public void createEmployee() { Employee emp = ...; // set some data em.joinTransaction(); // em created before JTA tx - manual join em.persist(emp); } // Transactional method public void updateEmployee() { Employee emp = em.find(...); // load the employee // don''t do join if both methods called in same session - can only call once: // em.joinTransaction(); // em created before JTA tx - manual join emp.set(...); // change some data // no persist call - automatically flushed with commit } @Remove // automatically called when EJB session ends public void cleanup() { em.close(); emf.close(); } // ... }

  • B) EJB con BEAN gestionado transacciones JTA

    Use @ TransactionManagement.BEAN.
    Inyecte la interfaz JTA UserTransaction, para que el bean pueda marcar directamente las transacciones de JTA.
    Marque / sincronice manualmente la transacción a través de UserTransaction.begin () / commit () / rollback ().
    Asegúrese de que EntityManager se una a la transacción JTA: cree el EM en un contexto de transacción JTA activo O llame a em.joinTransaction ().

    Ejemplos:

    @TransactionManagement(TransactionManagement.BEAN) @Stateless public class EmployeeServiceBean implements EmployeeService { // inject the JTA transaction interface @Resource UserTransaction jtaTx; public void createEmployee() { EntityManagerFactory emf = Persistence.createEntityManagerFactory("EmployeeService"); EntityManager em = emf.createEntityManager(); try { jtaTx.begin(); try { em.joinTransaction(); Employee emp = ...; // set some data em.persist(emp); // other data & em operations ... // call other EJBs to partake in same transaction ... } finally { jtaTx.commit(); } } catch (Exception e) { // handle exceptions from UserTransaction methods // ... } Employee emp = ...; // set some data // No need for manual join - em created in active tx context, automatic join: // em.joinTransaction(); em.persist(emp); em.close(); // Note: em can be closed before JTA tx committed. // Persistence Context will still exist inside JTA tx. // Data will still be flushed and committed and Persistence // Context removed some time later when tx is committed. emf.close(); } }

  • C) POJO / Non-EJB con transacciones locales de recursos codificados a mano (administrados con frijol) (no JTA)

    Simplemente use la interfaz JPA EntityTransaction para la demarcación de tx (obtenida a través de em.getTransaction ()).

    Ejemplo:

    public class ProjectServlet extends HttpServlet { @EJB ProjectService bean; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // ... try { EntityManagerFactory emf = Persistence.createEntityManagerFactory("myPU"); EntityManager em = emf.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); try { bean.assignEmployeeToProject(projectId, empId); bean.updateProjectStatistics(); } finally { tx.commit(); } } catch (Exception e) { // handle exceptions from EntityTransaction methods // ... } // ... } }

  • D) POJO / Non-EJB con transacciones JTA codificadas a mano (gestionadas por POJO)

    Esto supone que el POJO / componente se está ejecutando en algún contenedor que tenga compatibilidad con JTA.
    Si se encuentra en un contenedor Java EE, puede usar la inyección de recursos Java EE de la interfaz UserTransaction de JTA.
    (Alternativamente, puede buscar explícitamente un identificador en la interfaz JTA y hacer demarcación en él, luego llamar a em.getTransaction (). JoinTransaction () - ver especificaciones JTA).

    Ejemplo:

    public class ProjectServlet extends HttpServlet { @Resource UserTransaction tx; @EJB ProjectService bean; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // ... try { tx.begin(); try { bean.assignEmployeeToProject(projectId, empId); bean.updateProjectStatistics(); EntityManagerFactory emf = Persistence.createEntityManagerFactory("myPU"); EntityManager em = emf.createEntityManager(); // Should be able to avoid explicit call to join transaction. // Should automatically join because EM created in active tx context. // em.joinTransaction(); // em operations on data here em.close(); emf.close(); } finally { tx.commit(); } } catch (Exception e) { // handle exceptions from UserTransaction methods // ... } // ... } }