ユニットテストのためにJPAとHibernateを使用する1つのSpringアプリケーションを設定する際に問題があります。 私は2つのpersistence.xmlファイルをプロダクション用とユニットテスト用の2つ持っています。テスト用ユニットテスト(遅延ロード)のためにHibernateでSpring JPAアプリケーションを設定する
<?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>
:試験のため
<?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>
差は、私は、ローカル・トランザクションを使用し、私はJTA(グローバル・トランザクション)を使用いけないユニットテストです。
生産のためのバネ構成は次のとおりです。
<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/>
ユニットテストのための春の構成:
<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" />
これは、アプリケーションがあるため、グローバル・トランザクションを必要とする、私は、この構成のために私を決定するために同じ時間がかかりました私たちはJMSとDBの間のトランザクションを持っていますが、単体テストではローカルトランザクションのみを定義するので、アプリケーションのテストには限界があります。 この制限で私は単体テストを定義します。
今、私は関係のHibernateとLAZYの読み込みに問題があります。ユニットテストでは、メソッドの検索後にEntityManagerセッションが終了し、LAZYローディングのプロキシが機能していません(これは、Hibernateの定義どおりです) 私の問題は、ユニットテスト用に設定されたユニット名を持つBean PersistenceAnnotationBeanPostProcessorです@PersistenceContextというアノテーションを見つけるたびに、彼はテストのためにSpringコンフィグレーションで定義されたEntityManagerFactoryから作成された新しいEntityMangerを挿入しています。 は今ユニットテストは、あまりにも@PersistenceContext EntityManagerのメンバーとDAOクラスを持っている:
@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;
}
}
}
クラスSyDbVersionHomeがあまりにDBとEntityクラスからHibernateツールで生成されます。
問題は次の行です。 SyDbVersion instance = entityManager.find(SyDbVersion.class、id); findメソッドから戻ってくるたびに、セッションは閉じられ、怠け者はもう利用できなくなります。
永続ユニット名でPersistenceAnnotationBeanPostProcessorを正しく設定する方法を考えていましたが、BeanはJNDIで永続ユニットを検索していて、永続ユニットのJNDIエントリを登録する適切な時間を見つけることができません。
私はpersistent PersistenceAnnotationBeanPostProcessorを設定しているため、EntityManagerは適切に共有されているため、セッションはfind後に毎回閉じられません。
別の解決方法は、OpenEJBまたは埋め込みglassfishを使用して単体テストでアプリケーションサーバーをシミュレート/統合することです(統合テストとなります)。
単体テストでこの問題を回避するには、スプリング構成またはコードでどのようなオプションを変更する必要がありますか?