2012-02-24 16 views
6

Tengo un problema al configurar una aplicación Spring que está utilizando JPA con Hibernate para pruebas unitarias. Tengo 2 archivos persistence.xml, uno para producción y uno para pruebas unitarias. Para la prueba:Configurar las aplicaciones Spring JPA con Hibernate para pruebas unitarias (carga lenta)

<?xml version="1.0" encoding="UTF-8"?> 
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0"> 
    <persistence-unit name="prod_pu" transaction-type="JTA"> 
    <provider>org.hibernate.ejb.HibernatePersistence</provider> 
    <jta-data-source>jdbc/ds_prod</jta-data-source> 

    <properties> 
    <property name="hibernate.bytecode.use_reflection_optimizer" value="false"/> 
    <property name="hibernate.connection.driver_class" value="oracle.jdbc.OracleDriver"/> 
    <property name="hibernate.connection.password" value="passsample"/> 
    <property name="hibernate.connection.url" value="jdbc:oracle:thin:urlsample"/> 
    <property name="hibernate.connection.username" value="usernamesample"/> 
    <property name="hibernate.default_schema" value="schemassample"/> 
    <property name="hibernate.dialect" value="org.hibernate.dialect.OracleDialect"/> 
    </properties> 
    </persistence-unit> 

</persistence> 

para la prueba:

<?xml version="1.0" encoding="UTF-8"?> 
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0"> 

<persistence-unit name="test_pu" transaction-type="RESOURCE_LOCAL"> 
    <provider>org.hibernate.ejb.HibernatePersistence</provider> 
    <properties> 
    <property name="hibernate.bytecode.use_reflection_optimizer" value="false"/> 
    <property name="hibernate.connection.driver_class" value="oracle.jdbc.OracleDriver"/> 
    <property name="hibernate.connection.password" value="passsample"/> 
    <property name="hibernate.connection.url" value="jdbc:oracle:thin:urlsample"/> 
    <property name="hibernate.connection.username" value="usernamesample"/> 
    <property name="hibernate.default_schema" value="schemasample"/> 
    <property name="hibernate.dialect" value="org.hibernate.dialect.OracleDialect"/> 
    </properties> 
    </persistence-unit> 

</persistence> 

La diferencia está en pruebas unitarias No utilizo ningún JTA (transacciones globales), yo uso sólo las transacciones locales.

La configuración del resorte para la producción es:

<jee:jndi-lookup id="entityManagerFactory" jndi-name="persistence/ds_prod"/> 

<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"> 
    <property name="entityManagerFactory" ref="entityManagerFactory"/> 
</bean> 

<bean id="persAnnoBeanPostProc" class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" > 
    <property name="persistenceUnits"> 
    <map> 
     <entry key="prod_pu" value="persistence/prod_pu"/> 
    </map> 
    </property> 
</bean> 

<context:annotation-config/> 
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/> 
<context:component-scan base-package="com.sample.packagename" /> 
<tx:jta-transaction-manager/> 

La configuración de primavera para las pruebas unitarias:

<bean id="entityManagerFactory" 
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> 
    <!-- This workaround is necessary because Spring is buggy 
    Instead of including the test-classes/META-INF the spring always search into classes/META-INF and ignores the one from test-classes 
    --> 
    <property name="persistenceXmlLocation" value="META-INF/persistence-test.xml" /> 
    <property name="persistenceUnitName" value="test_pu" /> 
</bean> 

    <bean id="persAnnoBeanPostProc" class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" > 
    </bean> 

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> 
    <property name="entityManagerFactory" ref="entityManagerFactory" /> 
    <property name="persistenceUnitName" value="test_pu" /> 
    </bean> 

    <context:annotation-config /> 
    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/> 
    <context:component-scan base-package="com.sample.packagename" /> 

Me tomó mismo tiempo que me decida por esta configuración, las aplicaciones de las necesidades de transacciones global porque tenemos transacciones entre JMS y DB, pero en la prueba unitaria solo defino las transacciones locales, por lo que estoy limitado para probar la aplicación. Con estos límites, defino las pruebas de mi unidad.

Ahora tengo un problema con Hibernate y la carga de relaciones LAZY. En la prueba de Unidad, la sesión de EntityManager se cierra después de encontrar los métodos y luego el proxy para la carga LAZY no funciona (esto es por definición en Hibernate tal como se esperaba) Mi problema es Bean PersistenceAnnotationBeanPostProcessor doenst have any unitname set for unit tests y cada vez que encuentre la anotación @PersistenceContext, insertará un nuevo EntityManger creado a partir de EntityManagerFactory definido en la configuración del resorte para la prueba. Ahora la prueba unitaria es tener un miembro @PersistenceContext entityManager y la clase DAO también:

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(locations = {"classpath:testConfiguration.xml"}) 
@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true) 
@Transactional 
public class ConnectionTest { 

    @PersistenceContext 
    EntityManager entityManager; 

    Logger log = Logger.getLogger(ConnectionTest.class); 

    @Resource(name = "syDbVersionDao") 
    SyDbVersionDao dbVersionDao; 

    @Test 
    public void testChanging() { 
    String oldVer = dbVersionDao.getCurrentVersion(); 
    assertNotNull(oldVer); 
    } 
} 


@Component 
public class SyDbVersionDao extends SyDbVersionHome { 

@PersistenceContext 
private EntityManager entityManager; 

    public String getCurrentVersion() { 
     SyDbVersion res = getLastRecord(); 

     if (res == null) return ""; 
     return res.getVersion(); 
    } 

    public SyDbVersion getLastRecord(){ 
     Query query = entityManager.createQuery("from SyDbVersion v order by v.installationDate desc"); 
     query.setMaxResults(1); 
     return (SyDbVersion) query.getSingleResult(); 
    } 
} 


/** 
* Home object for domain model class SyDbVersion. 
* @see com.tsystems.ac.fids.web.persistence.jpa.SyDbVersion 
* @author Hibernate Tools, generated! 
*/ 
@Stateless 
public class SyDbVersionHome { 

    private static final Log log = LogFactory.getLog(SyDbVersionHome.class); 

    @PersistenceContext private EntityManager entityManager; 

    public void persist(SyDbVersion transientInstance) { 
     log.debug("persisting SyDbVersion instance"); 
     try { 
     entityManager.persist(transientInstance); 
     log.debug("persist successful"); 
     } 
     catch (RuntimeException re) { 
     log.error("persist failed", re); 
     throw re; 
     } 
    } 

    public void remove(SyDbVersion persistentInstance) { 
     log.debug("removing SyDbVersion instance"); 
     try { 
     entityManager.remove(persistentInstance); 
     log.debug("remove successful"); 
     } 
     catch (RuntimeException re) { 
     log.error("remove failed", re); 
     throw re; 
     } 
    } 

    public SyDbVersion merge(SyDbVersion detachedInstance) { 
     log.debug("merging SyDbVersion instance"); 
     try { 
     SyDbVersion result = entityManager.merge(detachedInstance); 
     log.debug("merge successful"); 
     return result; 
     } 
     catch (RuntimeException re) { 
      log.error("merge failed", re); 
      throw re; 
     } 
    } 

    public SyDbVersion findById(long id) { 
     log.debug("getting SyDbVersion instance with id: " + id); 
     try { 
      SyDbVersion instance = entityManager.find(SyDbVersion.class, id); 
      log.debug("get successful"); 
      return instance; 
     } 
     catch (RuntimeException re) { 
     log.error("get failed", re); 
     throw re; 
     } 
    } 
    } 

El SyDbVersionHome clase se genera con Hibernate Herramientas de la base de datos y la clase de entidad también.

Ahora el problema es la línea: SyDbVersion instance = entityManager.find (SyDbVersion.class, id); Cada vez que vuelvo del método find la sesión se cierra para que los miembros vagos ya no estén disponibles.

Estaba pensando en una forma de configurar correctamente el PersistenceAnnotationBeanPostProcessor con el nombre de la unidad persistente pero el bean busca la unidad de persistencia en JNDI y no puedo encontrar el momento adecuado para registrar una entrada JNDI para la unidad de persistencia.

En producción porque configuro persistir PersistenceAnnotationBeanPostProcessor EntityManager se comparte correctamente y la sesión no se cierra cada vez después de encontrar.

Otra solución será utilizar OpenEJB o glassfish incrustado para simular/tener un servidor de aplicaciones en las pruebas unitarias (se convertirán luego en pruebas de integración).

¿Qué opciones tengo que modificar en la configuración del resorte o en el código para evitar este problema en las pruebas unitarias?

Respuesta

1

Aparece el problema de mi código. El problema fue cuando el ApplicationContext se cargó, entonces estaba almacenando en caché algunas entidades JPA en un objeto Bean.Más tarde en el método de prueba (en otra transacción) estaba accediendo a estas relaciones de carga perezosas. Por supuesto, esto no funciona por diseño y debe acceder a la relación cargada con carga diferida en el mismo contexto de transacción; de lo contrario, obtendrá una excepción como en mi caso.

Solución:

  1. uso en lugar de LAZY carga, la carga ansiosos.

  2. inicializa explícitamente todas las asociaciones y colecciones necesarias antes de devolverlo. Luego puedes acceder a ellos en otra transacción también.

    Nodo n = // .. obtener el nodo

    Hibernate.initialize (n); // inicializa 'parent' similar a getParent.

    Hibernate.initialize (n.getChildren()); // pasa la colección perezosa a la sesión para inicializar.

  3. intenta mantener la sesión/transacción abierta hasta que la necesites. En mi caso no es realmente posible y no tiene sentido.

Cuestiones relacionadas