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