Announcement Announcement Module
Collapse
No announcement yet.
Having problems with JUnit/JPA with multiple persistence units Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Having problems with JUnit/JPA with multiple persistence units

    I am using Spring 3.0.1

    I have an existing project that uses JUnits extensively. I have recently tried adding a new JPA persistence unit in order to connect to a different database for logging (don't want to use our OLTP for logging ).

    I have searched the forums and the documentation (which is awesome BTW) and came up with the following solution:

    Two persistence units defined in a single persistence.xml file:
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <persistence xmlns="http://java.sun.com/xml/ns/persistence"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
        version="1.0">
        <persistence-unit name="Application" transaction-type="RESOURCE_LOCAL">
    		<properties>
    			<property name="hibernate.default_schema" ... value="dbo"/>
    
    			<!-- If true, Hibernate will display all generated SQL to the console -->			
                <property name="hibernate.show_sql" value="false" />
    			<!-- If true, Hibernate will format any console generated SQL (only makes sense if show_sql is set to true -->			
                <property name="hibernate.format_sql" value="false" />
    
    			<!-- Disable the second-level cache  -->
    			<property name="cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/>
    
                <!-- 2nd level cache  -->
    	<!--
                <property name="hibernate.cache.provider_class" value="net.sf.ehcache.hibernate.SingletonEhCacheProvider" />
                <property name="hibernate.cache.provider_configuration" value="/ehcache.xml" />
                <property name="hibernate.cache.use_second_level_cache" value="true" />
                <property name="hibernate.generate_statistics" value="true" />
                <property name="hibernate.cache.use_structured_entries" value="true" />
    	-->
    
            </properties>
        </persistence-unit>
    
        <persistence-unit name="Log4jLogging" transaction-type="RESOURCE_LOCAL">
    		<properties>
    			<property name="hibernate.default_schema" value="dbo"/>
    	
    			<!-- If true, Hibernate will display all generated SQL to the console -->			
                <property name="hibernate.show_sql" value="false" />
    			<!-- If true, Hibernate will format any console generated SQL (only makes sense if show_sql is set to true -->			
                <property name="hibernate.format_sql" value="false" />
    
    			<!-- Disable the second-level cache  -->
    			<property name="cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/>
    
                <!-- 2nd level cache  -->
    	<!--
                <property name="hibernate.cache.provider_class" value="net.sf.ehcache.hibernate.SingletonEhCacheProvider" />
                <property name="hibernate.cache.provider_configuration" value="/ehcache.xml" />
                <property name="hibernate.cache.use_second_level_cache" value="true" />
                <property name="hibernate.generate_statistics" value="true" />
                <property name="hibernate.cache.use_structured_entries" value="true" />
    	-->
    
            </properties>
        </persistence-unit>
    </persistence>
    Then I modifled my applicationContext.xml to have two datasource beans, two EntityManagerFactory beans and two transaction manager beans. I also defined the default persistenceUnit name (using a custom PersistenceAnnotationBeanPostProcessor) so that I could use @PersistenceContext without a unit name in most of the code. I also turned off annotation-config in the <context:component-scan> just to be sure that there would not be two PersistenceAnnotationBeanPostProcessors created (not sure if this was necessary or if Spring is smart enough not to create two).

    Code:
    .. see next post since this one was too long :mad:
    This all seems to work great when I run the code outside of JUnit (the proper EntityManager is injected, the proper transaction manager is used, etc).

    However when running JUnits, I am running into problems. The first is that Spring can't find the default persistence unit (the dreaded "2 beans found" error). I traced this back to the creation of a second PersistenceAnnotationBeanPostProcessor (called "org.springframework.context.annotation.internalPe rsistenceAnnotationProcessor"). This is created in the AbstractGenericContextLoader.loadContext() method during the call to:
    Code:
    	AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
    What I noticed is that this is part of the GenericXmlContextLoader that is used when doing an annotation based Spring JUnit setup, e.g.:
    Code:
    @ContextConfiguration(locations = { "file:conf/applicationContext.xml" })
    However, when running the code in production, we use FileSystemXmlApplicationContext which winds up using the base class AbstractXmlApplicationContext for most of the work. So I guess there are differencess between GenericApplicationContext and FileSystemXmlApplicationContext. In my case, the one differerence is that a "internalPersistenceAnnotationProcessor" is created that has no idea about a default persistence unit. I have found that if I change the @PersistenceContext in my JUnit classes to explicitly specify the unitName, e.g.:

    Code:
    @PersistenceContext(unitName = "Application")
    that the problem with the "internalPersistenceAnnotationProcessor" clogging up the works goes away.

    However, I was thinking that maybe:
    1. I am doing something fundamentally wrong (I'm leaning away from this since the Spring config seems fine in production runs).
    2. There is a bug in Spring, specifically in how the annotation configured Spring for JUnit is working (hey, I could be wrong ).

    I'm having another problem, which is that Spring cannot find a transaction manager named "transactionManager" (not suprising since neither of my transaction managers are named "transactionManager"). However, I have not fully investigated this issue yet, and it may turn out that there is a simple solution to this.

  • #2
    Having problems with JUnit/JPA with multiple persistence units (continued...)

    Here is the applicationContext.xml file that I could not fit in the last post:

    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    
    <beans 
    	xmlns="http://www.springframework.org/schema/beans" 
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    	xmlns:aop="http://www.springframework.org/schema/aop" 
    	xmlns:util="http://www.springframework.org/schema/util" 
    	xmlns:jndi="http://www.springframework.org/schema/jee"
    	xmlns:tx="http://www.springframework.org/schema/tx" 
    	xmlns:context="http://www.springframework.org/schema/context"
    
    	xsi:schemaLocation="
    		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
    		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
    		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd
    		http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
    		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
    		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
    	">
    
    	<!-- 
    		Allow us to access properties defined using ANT style of ${some.property.name} through a
    		configurer that replaces ${...} placeholders with values from a properties file
    		(in this case, JDBC-related settings for the dataSource definition below).
    	-->
    	<context:property-placeholder location="file:conf/jdbc.properties"/>
    
    	<!-- 
    		Process @Repository to use Spring exception translation.
    		
    		http://static.springframework.org/spring/docs/2.5.x/reference/orm.html#orm-jpa-exceptions
    		http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/dao/annotation/PersistenceExceptionTranslationPostProcessor.html
    	-->
    	<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
    
    	<!-- 
    		Create the default JPA EntityManagerFactory.  
    		This links together the datasource and the JPA provider.
    		
    		http://static.springframework.org/spring/docs/2.5.x/reference/orm.html#orm-jpa-setup-lcemfb
    		http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBean.html
    	-->
    	<bean id="applicationEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    		<!-- 
    			It appears that load time weaving is not necessary for Hibernate, but this may be revisited if things stop working.
    			http://static.springframework.org/spring/docs/2.5.x/reference/orm.html#orm-jpa-setup-lcemfb
    			
    			<property name="loadTimeWeaver">
    				<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
    			</property>
    		 -->
    		<property name="dataSource" ref="applicationDataSource"/>
    		<property name="persistenceUnitName" value="Application"/>
    		<property name="jpaVendorAdapter">
    			<!-- 
    				Configure Hibernate as the JPA vendor provider with SQL Server as the dialect.
    			-->
    			<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
    				<property name="database" value="SQL_SERVER"/>
    				<property name="generateDdl" value="false" />
    			</bean>
    		</property>
    	</bean>
    
    	<!-- 
    		Create the JPA EntityManagerFactory.  
    		This links together the datasource and the JPA provider.
    		
    		http://static.springframework.org/spring/docs/2.5.x/reference/orm.html#orm-jpa-setup-lcemfb
    		http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBean.html
    	-->
    	<bean id="loggingEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    		<!-- 
    			It appears that load time weaving is not necessary for Hibernate, but this may be revisited if things stop working.
    			http://static.springframework.org/spring/docs/2.5.x/reference/orm.html#orm-jpa-setup-lcemfb
    			
    			<property name="loadTimeWeaver">
    				<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
    			</property>
    		 -->
    		<property name="dataSource" ref="loggingDataSource"/>
    		<property name="persistenceUnitName" value="Log4jLogging"/>
    		<property name="jpaVendorAdapter">
    			<!-- 
    				Configure Hibernate as the JPA vendor provider with SQL Server as the dialect.
    			-->
    			<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
    				<property name="database" value="SQL_SERVER"/>
    				<property name="generateDdl" value="false" />
    			</bean>
    		</property>
    	</bean>
    
    	<!-- 
    		Create a pooled data source as the default application datasource.
    		Also configure it with an alias following the pattern of datasource.XXX.
    	-->
    	<bean id="applicationDataSource" name="datasource.app" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    		<property name="driverClass" value="net.sourceforge.jtds.jdbc.Driver"/>
    		<!-- 
    			"prepareSQL=3" "will cause the driver to cache column meta data for SELECT statements. 
    			Caching the meta data will reduce the processing overhead when reusing statements that return small result sets that have many columns 
    			but may lead to unexpected errors if the database schema changes after the statement has been prepared." - jtds documentation
    			
    			Since our schema never changes, we should be ok.
    		-->
    		<!-- Inject the values imported by the property-placeholder above. -->
    		<property name="jdbcUrl" value="jdbc:jtds:sqlserver://${database.machinename}/${database.name};instance=${database.instance};sendStringParametersAsUnicode=false;cacheMetaData=true;prepareSQL=3"/>
    		<property name="initialPoolSize" value="4"/>
    		<property name="minPoolSize" value="4"/>
    		<property name="maxPoolSize" value="50"/>
    		<property name="maxStatementsPerConnection" value="500"/>
    		<property name="acquireIncrement" value="2"/>
    		<!-- On loss of database connectivity, try to connect again indefinitely every 5 seconds -->
    		<property name="acquireRetryAttempts" value="0"/> 
    		<property name="acquireRetryDelay" value="5000"/>
    		<property name="maxIdleTimeExcessConnections" value="1800"/>
    		<property name="idleConnectionTestPeriod" value="300"/>
    		<property name="preferredTestQuery" value="select 'Hibernate/C3P0 connection test'"/>
    	</bean>
    
    	<!-- 
    		Create a pooled data source as the logging datasource.
    		Also configure it with an alias following the pattern of datasource.XXX to make it suitable for automatic detection by the dbs stuff)  
    	-->
    	<bean id="loggingDataSource" name="datasource.logging" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    		<property name="driverClass" value="net.sourceforge.jtds.jdbc.Driver"/>
    		<!-- 
    			"prepareSQL=3" "will cause the driver to cache column meta data for SELECT statements. 
    			Caching the meta data will reduce the processing overhead when reusing statements that return small result sets that have many columns 
    			but may lead to unexpected errors if the database schema changes after the statement has been prepared." - jtds documentation
    			
    			Since our schema never changes, we should be ok.
    		-->
    		<!-- Inject the values imported by the property-placeholder above. -->
    		<property name="jdbcUrl" value="jdbc:jtds:sqlserver://${database.machinename}/${database.name};instance=${database.instance};sendStringParametersAsUnicode=false;cacheMetaData=true;prepareSQL=3"/>
    		<property name="initialPoolSize" value="4"/>
    		<property name="minPoolSize" value="4"/>
    		<property name="maxPoolSize" value="50"/>
    		<property name="maxStatementsPerConnection" value="500"/>
    		<property name="acquireIncrement" value="2"/>
    		<!-- On loss of database connectivity, try to connect again indefinitely every 5 seconds -->
    		<property name="acquireRetryAttempts" value="0"/> 
    		<property name="acquireRetryDelay" value="5000"/>
    		<property name="maxIdleTimeExcessConnections" value="1800"/>
    		<property name="idleConnectionTestPeriod" value="300"/>
    		<property name="preferredTestQuery" value="select 'Hibernate/C3P0 connection test'"/>
    	</bean>
    	
    	<!--
    		Create a transaction manager for JPA operations that exposes JPA transactions to the underlying
    		JDBC connection.
    		
    		http://static.springframework.org/spring/docs/2.5.x/reference/orm.html#orm-jpa-tx 
    		http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/orm/jpa/JpaTransactionManager.html
    	-->
    	<bean id="applicationTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    		<property name="entityManagerFactory" ref="applicationEntityManagerFactory"/>
    	</bean>
    
    	<bean id="loggingTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    		<property name="entityManagerFactory" ref="loggingEntityManagerFactory"/>
    	</bean>
    
    	<!--
    		Register the bean post-processor for JPA annotations; this supports @ApplicationContext(unitName = "someName")
    		
    		Also supply a default persistence unit name (in this case "Application")
    		
    		http://static.springframework.org/spring/docs/2.5.x/reference/orm.html#orm-jpa-straight 
    		http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.html
    	-->
    	<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor">
    		<property name="defaultPersistenceUnitName" value="Application"/>
    	</bean>
    Can't fit the whole applicationContext.xml in a single post . The next post will have the rest of the file...

    Comment


    • #3
      Having problems with JUnit/JPA with multiple persistence units (continued...)

      Here is the rest of the applicationContext.xml file.

      Code:
      	<!-- The following bean post-processors need to be registered explicitly because we have turned annotation-config 
      		off in the component-scan part.  This was done so that a custom PersistenceAnnotationBeanPostProcessor could be defined.
      	-->
      	<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
      	<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/>
      	<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>
      
      	<!-- 
      		Process @Transactional to create transaction proxies 
      		
      		http://static.springframework.org/spring/docs/2.5.x/reference/transaction.html#transaction-declarative-annotations
      	-->
      	<tx:annotation-driven transaction-manager="applicationTransactionManager"/>
      	<tx:annotation-driven transaction-manager="loggingTransactionManager"/>
      
      	<!-- 
      		Configure classpath scanning for annotated components. 
      
      		This tag implies the effects of the 'annotation-config' tag, activating @Required,
      		@Autowired, @PostConstruct, @PreDestroy, @Autowired, @PersistenceContext and @PersistenceUnit
      		annotations in the component classes, which is usually desired for autodetected components
      		(without external configuration). Turn off the 'annotation-config' attribute to deactivate
      		this default behavior, for example in order to use custom BeanPostProcessor definitions
      		for handling those annotations.
      
      		http://static.springsource.org/spring/docs/2.5.x/reference/beans.html#beans-scanning-autodetection
      	-->
      	
      	<context:component-scan base-package="com.somecompany.stuff" annotation-config="false"/>
      I guess the basic question for this whole post is:


      Anyone else using Spring annotation based JUnits with multiple persistence units and if so is it working correctly for you? If so, how did you do it differently that I did?


      Or did you run into the same problems and have to use the same workarounds (named persistence unitName in all JUnits and changing the name of the "default" transaction manager to "transactionManager")?

      Also, do any Spring developers monitor this forum? Should this be reported as a bug in JIRA?

      In addition, even if I don't get much of an answer, hopefully all this configuration file stuff that I posted might be helpful to someone else struggling with Spring/Hibernate/JPA configuration.

      Comment

      Working...
      X