2012-02-24 27 views
6

Mam problem z konfiguracją jednej aplikacji Spring, która używa JPA z Hibernate do testowania jednostkowego. Mam 2 pliki persistence.xml jeden do produkcji i jeden do testów jednostkowych. Do testów:Konfigurowanie aplikacji Spring JPA z Hibernate do testowania jednostkowego (ładowanie z opóźnieniem)

<?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> 

do testowania:

<?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> 

Różnica jest w testach jednostkowych dont używać żadnych transakcji JTA (Global), używam tylko lokalne transakcje.

Konfiguracja wiosna dla produkcji jest:

<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/> 

Konfiguracja sprężynowe dla testów jednostkowych:

<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" /> 

zajęło mi tyle samo czasu na decydowania mnie do tej konfiguracji aplikacji potrzebuje globalnych transakcji, ponieważ mamy transakcje między JMS i DB, ale w teście jednostki definiuję tylko transakcje lokalne, więc jestem ograniczony w testowaniu aplikacji. Przy tych limitach definiuję moje testy jednostkowe.

Teraz mam problem z Hibernate i LAZY ładowanie relacji. W teście Unit sesja EntityManager zamyka się po znalezieniu metod, a następnie i proxy dla LAZY ładowanie nie działa. (To jest z definicji w Hibernate zgodnie z oczekiwaniami) Moim problemem jest Perspektywa fasoliNarzędziPrezentacjaBeanPostProcessor ma jakikolwiek zestaw nazw jednostkowych dla testów jednostkowych i za każdym razem, gdy znajduje adnotację @PersistenceContext wstawia nowy EntityManger stworzony z EntityManagerFactory zdefiniowany w konfiguracji wiosennej do testowania. Teraz test jednostki jest posiadanie elementu @PersistenceContext EntityManager i klasy DAO TOO:

@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; 
     } 
    } 
    } 

Klasa SyDbVersionHome jest generowany z Hibernate Tools z DB i klasy Entity też.

Teraz problemem jest wiersz: Instancja SyDbVersion = entityManager.find (SyDbVersion.class, id); Za każdym razem, gdy wracam z metody szukania, sesja jest zamknięta, więc leniwi członkowie nie są już dostępni.

Zastanawiam się, jak poprawnie skonfigurować parametr PersistenceAnnotationBeanPostProcessor z nazwą jednostki trwałej, ale komponent bean wyszukuje jednostkę trwałości w JNDI i nie można znaleźć odpowiedniego czasu na zarejestrowanie wpisu JNDI dla jednostki utrwalania.

W produkcji, ponieważ ustawiłem parametr PersistenceAnnotationBeanPostProcessor, EntityManager jest następnie poprawnie udostępniany, a sesja nie jest zamykana za każdym razem po znalezieniu.

Innym rozwiązaniem będzie użycie OpenEJB lub embedded-glassfish do symulacji/posiadania serwera aplikacji w testach jednostkowych (staną się wtedy testami integracyjnymi).

Jakie opcje muszę zmodyfikować w konfiguracji sprężynowej lub w kodzie, aby uniknąć tego problemu podczas testowania urządzenia?

Odpowiedz

1

Dowiedziałem się o problemie mojego kodu. Problem polegał na tym, że załadowano ApplicationContext, a następnie buforowałem niektóre jednostki JPA w obiekcie Bean.Później w metodzie testowej (w innej transakcji) uzyskiwałem dostęp do tych leniwych relacji ładujących. Oczywiście nie działa to zgodnie z projektem i musisz uzyskać dostęp do relacji załadowanej z leniwym ładowaniem w tym samym kontekście transakcji, w przeciwnym razie otrzymasz wyjątek taki jak w moim przypadku.

Rozwiązanie:

  1. użycie zamiast leniwy załadunku, chętny do załadunku.

  2. jawnie zainicjować wszystkie powiązania i kolekcje wymagane przed zwróceniem. Następnie możesz uzyskać do nich dostęp w innej transakcji.

    węzła N = // .. się węzeł

    Hibernate.initialize (n); // inicjuje "rodzic" podobny do getParent.

    Hibernate.initialize (n.getChildren()); // przekaż leniwą kolekcję do sesji, aby ją zainicjować.

  3. spróbuj utrzymać sesję/transakcję otwartą, aż jej potrzebujesz. W moim przypadku nie jest to możliwe i nie ma sensu.

Powiązane problemy