2012-02-24 27 views
6

Tôi gặp sự cố khi định cấu hình một ứng dụng Spring đang sử dụng JPA với Hibernate để kiểm tra đơn vị. Tôi đang có 2 tệp persistence.xml một cho sản xuất và một cho các bài kiểm tra đơn vị. Để thử nghiệm:Định cấu hình các ứng dụng Spring JPA với Hibernate để kiểm tra đơn vị (lazy-loading)

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

để thử nghiệm:

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

Sự khác biệt là trong các thử nghiệm đơn vị tôi không sử dụng bất kỳ JTA (giao dịch toàn cầu), tôi chỉ sử dụng các giao dịch tại địa phương.

Cấu hình mùa xuân phục vụ sản xuất là:

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

Cấu hình mùa xuân cho unit tests:

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

Nó đã cho tôi cùng một thời gian để quyết định tôi cho cấu hình này, các ứng dụng cần giao dịch toàn cầu vì chúng tôi có các giao dịch giữa JMS và DB nhưng trong bài kiểm tra đơn vị, tôi chỉ xác định các giao dịch địa phương nên tôi bị giới hạn trong việc thử nghiệm ứng dụng. Với giới hạn này, tôi xác định các bài kiểm tra đơn vị của mình.

Bây giờ tôi gặp sự cố với tải quan hệ Hibernate và LAZY. Trong bài kiểm tra Unit, Session EntityManager đóng sau khi tìm các phương thức và sau đó và proxy cho tải LAZY không hoạt động (điều này theo định nghĩa trong Hibernate như mong đợi) Vấn đề của tôi là Bean PersistenceAnnotationBeanPostProcessor nó doenst có bất kỳ unitname nào cho các unit tests và bất cứ khi nào anh ta tìm thấy chú thích @PersistenceContext, anh ta đang chèn một EntityManger mới được tạo từ EntityManagerFactory được định nghĩa trong cấu hình mùa xuân để thử nghiệm. Bây giờ kiểm tra đơn vị là có một thành viên @PersistenceContext EntityManager và lớp DAO quá:

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

Các SyDbVersionHome lớp được tạo ra với Hibernate Công cụ từ DB và lớp Entity quá.

Bây giờ vấn đề là dòng: SyDbVersion instance = entityManager.find (SyDbVersion.class, id); Mỗi khi tôi quay trở lại từ phương pháp tìm, phiên họp đã bị đóng để các thành viên lười biếng không còn nữa. Tôi đã suy nghĩ một cách để cấu hình đúng PersistenceAnnotationBeanPostProcessor với tên đơn vị vẫn tồn tại nhưng bean đang tìm kiếm sau đó đơn vị persistence trong JNDI và tôi không thể tìm thấy thời điểm thích hợp để đăng ký một mục JNDI cho thiết bị persistence.

Trong sản xuất vì tôi đặt PersistenceAnnotationBeanPostProcessor PersityManager sau đó được chia sẻ đúng cách và phiên không được đóng mỗi lần sau khi tìm.

Một giải pháp khác là sử dụng OpenEJB hoặc thủy tinh nhúng để mô phỏng/có một máy chủ ứng dụng trong các bài kiểm tra đơn vị (sau đó sẽ trở thành kiểm thử tích hợp).

Tùy chọn nào tôi phải sửa đổi trong cấu hình mùa xuân hoặc trong mã để tránh sự cố này trong thử nghiệm đơn vị?

Trả lời

1

Tôi tìm ra vấn đề về mã của mình. Vấn đề là khi ApplicationContext được nạp sau đó tôi đã Caching một số thực thể JPA trong một Bean Object.Sau đó trong phương pháp thử nghiệm (trong giao dịch khác) tôi đã accesing này tải lười biếng quan hệ. Tất nhiên điều này là không làm việc bằng cách thiết kế và bạn phải truy cập các mối quan hệ tải với tải lười biếng trong bối cảnh giao dịch tương tự nếu không bạn sẽ có được một ngoại lệ như trong trường hợp của tôi.

Giải pháp:

  1. sử dụng thay vì lười tải, háo hức tải.

  2. khởi tạo rõ ràng tất cả các liên kết và bộ sưu tập cần thiết trước khi trả lại. Sau đó, bạn có thể truy cập chúng trong một giao dịch khác.

    Node n = // .. nhận nút

    Hibernate.initialize (n); // khởi tạo 'parent' tương tự với getParent.

    Hibernate.initialize (n.getChildren()); // chuyển bộ sưu tập lười vào phiên được khởi tạo.

  3. cố gắng giữ phiên/giao dịch mở cho đến khi bạn cần. Trong trường hợp của tôi là không thực sự có thể và không có ý nghĩa.

Các vấn đề liên quan